<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>yayz's devlog</title>
    <link>https://yay-dev.tistory.com/</link>
    <description>Devops &amp;amp; Backend Developer | tech blog</description>
    <language>ko</language>
    <pubDate>Fri, 26 Jun 2026 15:55:22 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>bandal-gom</managingEditor>
    <image>
      <title>yayz's devlog</title>
      <url>https://tistory1.daumcdn.net/tistory/2982625/attach/d628813b2f0d4340824983330dde39dc</url>
      <link>https://yay-dev.tistory.com</link>
    </image>
    <item>
      <title>99클럽 코테 스터디 16일차 TIL</title>
      <link>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-16%EC%9D%BC%EC%B0%A8-TIL</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;뜨문뜨문한 TIL...이대로 괜찮은가?  &lt;/blockquote&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25757&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백준 25757 &amp;ndash; 임스와 친구들&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.com/problems/number-of-1-bits/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LeetCode - Number of 1 Bits&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;백준 25757 &amp;ndash; 임스와 친구들&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제요약&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임 종류(Y/F/O)에 따라 참여 인원 수가 다름 (2/3/4)&lt;/li&gt;
&lt;li&gt;중복된 닉네임 제거 후, 최대 몇 게임 가능한지 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;풀이 전략&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값을 받은 후, set으로 중복된 닉네임 제거&lt;/li&gt;
&lt;li&gt;게임에 따라 추가로 필요한 인원수를 dict로 저장&amp;nbsp;&lt;/li&gt;
&lt;li&gt;중복이 제거된 플레이어 이름에서 선택된 게임에서 필요한 인원수를 나눈값만큼 게임 플레이 가능&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드&lt;/h4&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys

prmpt = sys.stdin.readline().split()
names = [sys.stdin.readline().strip() for _ in range(int(prmpt[0]))]

games = {'Y': 1, 'F': 2, 'O': 3}
curr_game = games[prmpt[1]]

names_set = set(names)
print(len(names_set)//curr_game)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 191 &amp;ndash; Number of 1 Bits&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제 요약&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정수 n에서 이진수로 나타낸 1의 개수 세기 (= Hamming Weight)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;풀이 전략&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bin 함수를 사용해서 이진수로 변환 후, 1의 개수 세기&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;amp;, &amp;gt;&amp;gt; 연산자를 사용해서 1의 개수 카운트하는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n &amp;amp; 1로 마지막 bit 검사&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;gt;&amp;gt;로 비트 오른쪽으로 쉬프트&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드&lt;/h4&gt;
&lt;pre id=&quot;code_1744725990248&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bin, count 함수 사용 
class Solution:
    def hammingWeight(self, n: int) -&amp;gt; int:
        return bin(n).count('1')

# bit operation 
class Solution:
    def hammingWeight(self, n: int) -&amp;gt; int:
        count = 0
        while n:
            count += n &amp;amp; 1
            n &amp;gt;&amp;gt;= 1
        return count&lt;/code&gt;&lt;/pre&gt;</description>
      <category>devlog/TIL</category>
      <category>99클럽</category>
      <category>LeetCode</category>
      <category>leetcode 191</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>백준</category>
      <category>백준 25757</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/176</guid>
      <comments>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-16%EC%9D%BC%EC%B0%A8-TIL#entry176comment</comments>
      <pubDate>Tue, 15 Apr 2025 23:08:08 +0900</pubDate>
    </item>
    <item>
      <title>99클럽 코테 스터디 11일차 TIL</title>
      <link>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-11%EC%9D%BC%EC%B0%A8-TIL</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;오늘의 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/design-hashmap/description/&quot;&gt;706. Design HashMap&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;직접 해시맵 구현하기 (내장 hashmap 라이브러리 사용X)&lt;/li&gt;
&lt;li&gt;key 범위: &lt;code&gt;0 &amp;lt;= key &amp;lt;= 10^6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;최대 10^4번의 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이 전략&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;튜플 사용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고정 크기 버킷 배열 사용: &lt;code&gt;self.map = [...]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;충돌 해결 방식 &amp;rarr; 각 버킷에 &lt;code&gt;(key, value)&lt;/code&gt; 튜플을 리스트로 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;class MyHashMap:

    def __init__(self):
        self.size = 10000 # 10^4 operation 대비? 
        self.map = [[] for _ in range(self.size)]

    def put(self, key: int, value: int) -&amp;gt; None:
        index = hash(key) % self.size
        for i , (k,v) in enumerate(self.map[index]):
            if k == key:
                self.map[index][i] = (key, value)
                return
        self.map[index].append((key, value))

    def get(self, key: int) -&amp;gt; int:
        index = hash(key) % self.size
        for i , (k,v) in enumerate(self.map[index]):
            if k == key: 
                return v
        return -1

    def remove(self, key: int) -&amp;gt; None:
        index = hash(key) % self.size
        self.map[index] = [(k,v) for (k,v) in self.map[index] if k != key]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LinkedList, Node 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열 index당 LinkedList가 생성되는 구조 (Java의 Hashmap구조와 비슷..?)&lt;/li&gt;
&lt;li&gt;단점: 구현해야하는 것이...조금 귀찮다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;class MyHashMap:

    def __init__(self, size=10000):
        self.map = [LinkedList() for _ in range(size)]       

    def put(self, key: int, value: int) -&amp;gt; None:
        index = hash(key) % len(self.map)
        self.map[index].insert(key, value)        

    def get(self, key: int) -&amp;gt; int:
        index = hash(key) % len(self.map)
        ret = self.map[index].get(key)        
        if ret == None: 
            return -1
        else: 
            return ret

    def remove(self, key: int) -&amp;gt; None:
        index = hash(key) % len(self.map)
        self.map[index].remove(key)

class LinkedList:
    def __init__(self): 
        self.head = None 

    def insert(self, key, value): 
        node = self.head
        while node: 
            if node.key == key:
                node.value = value
                return 
            node = node.next
        new_node = Node(key, value, self.head)
        self.head = new_node

    def get(self, key): 
        node = self.head
        while node: 
            if node.key == key:
                return node.value 
            node = node.next 
        return None

    def remove(self, key): 
        prev = None
        node = self.head
        while node: 
            if node.key == key:
                if prev: 
                    prev.next = node.next
                else: 
                    self.head = node.next
                return 
            prev = node
            node = node.next 

class Node: 
    def __init__(self, key, value, next=None):
        self.key = key
        self.value = value
        self.next = next&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python: 배운 문법 &amp;amp; 구조&lt;/h2&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;code&gt;[...]&lt;/code&gt; 바깥이 최종 리스트&lt;/li&gt;
&lt;li&gt;안에 &lt;code&gt;for&lt;/code&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;code&gt;(key, value)&lt;/code&gt; 형태&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(1,)&lt;/code&gt; &amp;larr; 하나만 있을 땐 쉼표 필요&lt;/li&gt;
&lt;li&gt;리스트 안에 튜플 넣어서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;remove()&lt;/code&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;code&gt;list.remove()&lt;/code&gt;는 전체 튜플을 알아야 동작함 &amp;rarr; value 모르면 불가능&lt;/li&gt;
&lt;li&gt;그래서 조건 필터링으로 새 리스트 생성하는 방식이 현실적&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;성능 관련 고찰&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제점:&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;get()&lt;/code&gt; / &lt;code&gt;put()&lt;/code&gt; / &lt;code&gt;remove()&lt;/code&gt; 전부 충돌 시 리스트 선형 탐색 &amp;rarr; O(n)&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;b&gt;버킷 수 늘리기&lt;/b&gt;: 10007, 20011 같은 &lt;b&gt;소수&lt;/b&gt; 추천&lt;/li&gt;
&lt;li&gt;&lt;b&gt;튜플 대신 Node 클래스 쓸 수도 있지만, 느림&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Open Addressing 방식&lt;/b&gt;은 구현 복잡하지만 더 빠름 -&amp;gt; dict가 이런 방식으로 구현되어있다고 함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>devlog/TIL</category>
      <category>99클럽</category>
      <category>hashmap 구현</category>
      <category>leetcode 706</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/175</guid>
      <comments>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-11%EC%9D%BC%EC%B0%A8-TIL#entry175comment</comments>
      <pubDate>Thu, 10 Apr 2025 23:53:41 +0900</pubDate>
    </item>
    <item>
      <title>99클럽 코테 스터디 10일차 TIL</title>
      <link>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9D%BC%EC%B0%A8-TIL</link>
      <description>&lt;h2&gt;오늘의 문제&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/top-k-frequent-elements/description/&quot;&gt;LeetCode 347: Top K Frequent Elements&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://leetcode.com/problems/check-if-number-has-equal-digit-count-and-digit-value/description/&quot;&gt;LeetCOde 2283: Check if Number Has Equal Digit Count and Digit Value&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;문제 요약&lt;/h2&gt;
&lt;p&gt;Top K Frequent Elements &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nums 배열에서 가장 많이 등장한 k개의 숫자 return (배열로) &lt;/li&gt;
&lt;li&gt;시간복잡도 O(nlogn) &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;풀이 전략&lt;/h2&gt;
&lt;p&gt;시간복잡도 O(nlogn) 이라, brute-force는 불가능&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Counter 함수와 Heap을 사용해서 풀이&lt;/li&gt;
&lt;li&gt;Counter 함수: 주어진 배열에서의 빈도수를 dictionary 형태로 생성 -&amp;gt; 시간복잡도 O(n)&lt;/li&gt;
&lt;li&gt;Heap: 빈도 높은 k개를 추출하기 위해 사용 -&amp;gt; 시간복잡도 O(nlogn) &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;코드&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from collections import Counter
import heapq 

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -&amp;gt; List[int]:
        count_dict = Counter(nums)
        return heapq.nlargest(k, count_dict.keys(), key=count_dict.get)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;heapq.nlargest(): 빈도 높은 k개의 요소 추출&lt;/li&gt;
&lt;li&gt;count_dict.keys() 키값을 기준으로 iterate&lt;/li&gt;
&lt;li&gt;count_dict.get (=value, 즉 빈도수) 를 비교 기준으로 삼아서 k개 추출 &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;고민&lt;/h2&gt;
&lt;p&gt;함수만 사용해서 구현한것같은데 이게 맞나 싶음&lt;br&gt;직접 구현한 내용도 추가해보자 &lt;/p&gt;</description>
      <category>devlog/TIL</category>
      <category>99클럽</category>
      <category>leetcode 347</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/174</guid>
      <comments>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9D%BC%EC%B0%A8-TIL#entry174comment</comments>
      <pubDate>Wed, 9 Apr 2025 23:56:22 +0900</pubDate>
    </item>
    <item>
      <title>NHN FORWARD 2022 발표 후기</title>
      <link>https://yay-dev.tistory.com/entry/NHN-FORWARD-2022-%EB%B0%9C%ED%91%9C-%ED%9B%84%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  25년에 올리는 22년도의 발표후기 (...) 22년도에 따끈하게 작성해두고 비공개 처리 해둔 글을 발견해서 다시 재발행 합니다... 유튜브 링크도 올라와서 추가했습니다!&amp;nbsp;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;내가 발표라니?!?!?   한번쯤은 해보고 싶다..라는 생각이 들었었지만 이렇게 하게 될 줄은 몰랐다! 우당탕탕 준비 하기는 했지만 겪어 보고 나니 정말 좋은 경험을 했다!&amp;nbsp;&lt;/blockquote&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;981293CE-B0E0-4973-BD7F-559A1511D201.JPG&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5eNDK/btrR22eNzWb/165HzeXtqQfdJ8T9uCeiR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5eNDK/btrR22eNzWb/165HzeXtqQfdJ8T9uCeiR0/img.jpg&quot; data-alt=&quot;3년 만의 오프라인 컨퍼런스!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5eNDK/btrR22eNzWb/165HzeXtqQfdJ8T9uCeiR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5eNDK%2FbtrR22eNzWb%2F165HzeXtqQfdJ8T9uCeiR0%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;682&quot; height=&quot;682&quot; data-filename=&quot;981293CE-B0E0-4973-BD7F-559A1511D201.JPG&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3년 만의 오프라인 컨퍼런스!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NHN FORWARD??&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NHN에서 주최하는 기술 컨퍼런스이다. 2018년 부터 매년 주최하였고, 코로나로 인해 20년,21년은 온라인으로 진행되었다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BE, FE, 그리고 앱개발 등 기술적인 내용을 발표하는 자리이기도 하고, 라운지 토크 처럼 시간은 라이트하지만 내용은 알찬! 세션들도 준비되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://forward.nhn.com/2022&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://forward.nhn.com/2022&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669340765303&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;NHN FORWARD 22&quot; data-og-description=&quot;기술로 더 나은 세상을 만들어 가는 사람들과 함께하는 NHN 기술 콘퍼런스 - 11월 24일에 만나요.&quot; data-og-host=&quot;forward.nhn.com&quot; data-og-source-url=&quot;https://forward.nhn.com/2022&quot; data-og-url=&quot;https://forward.nhn.com/2022&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bpgPWP/hyQGpakpOz/5FIhoahi2ThavvSSo5Zyhk/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/mAOpN/hyQGm5Kpjj/NjHPLXEigvY4vOLelwETN1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/MMxbG/hyQGvn5RtY/3WDgQivntU5nWiQ2KSartK/img.png?width=720&amp;amp;height=1020&amp;amp;face=0_0_720_1020&quot;&gt;&lt;a href=&quot;https://forward.nhn.com/2022&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://forward.nhn.com/2022&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bpgPWP/hyQGpakpOz/5FIhoahi2ThavvSSo5Zyhk/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/mAOpN/hyQGm5Kpjj/NjHPLXEigvY4vOLelwETN1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/MMxbG/hyQGvn5RtY/3WDgQivntU5nWiQ2KSartK/img.png?width=720&amp;amp;height=1020&amp;amp;face=0_0_720_1020');&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;NHN FORWARD 22&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기술로 더 나은 세상을 만들어 가는 사람들과 함께하는 NHN 기술 콘퍼런스 - 11월 24일에 만나요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;forward.nhn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전년도 컨퍼런스는 사이트 하단에 링크 되어있다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제가 발표한 것은.. Jenkins에서 Jenkins로의 여정!&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1077.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/b0GyG5/btrR2RRZAnp/Yc4i4tBlEjZKwi5KkP2TAK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0GyG5/btrR2RRZAnp/Yc4i4tBlEjZKwi5KkP2TAK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0GyG5/btrR2RRZAnp/Yc4i4tBlEjZKwi5KkP2TAK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0GyG5%2FbtrR2RRZAnp%2FYc4i4tBlEjZKwi5KkP2TAK%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;521&quot; height=&quot;695&quot; data-filename=&quot;IMG_1077.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;현재 팀에서 진행했던 CI (= Jenkins)를 개선한 내용에 대한 경험을 공유하는 발표를 하였다. Jenkins X, Tekton, 그리고 Kubernetes Jenkins에 대해서 조사하고, 고민했던 내용들을 주로 얘기했다. 발표에 대한 내용은 아마 유튜브에 올라오게 될텐데, 업로드가 되는대로 본문에 링크 추가하겠습니다 ~.~&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_edited_IMG_1113.JPG&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0vT9I/btrR2xl60QW/7IuAZ78kzd8uuIEh424SPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0vT9I/btrR2xl60QW/7IuAZ78kzd8uuIEh424SPk/img.png&quot; data-alt=&quot;너무나도 귀여웠던 뒷풀이 배너&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0vT9I/btrR2xl60QW/7IuAZ78kzd8uuIEh424SPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0vT9I%2FbtrR2xl60QW%2F7IuAZ78kzd8uuIEh424SPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;775&quot; data-filename=&quot;edited_edited_IMG_1113.JPG&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;너무나도 귀여웠던 뒷풀이 배너&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;떨리기도 떨렸고, 교수법 강의를 진행했을 때, 그리고 지금까지 했던 리허설 환경이랑은 아무래도 좀 다른 감이 있었다. 예를 들면, 교수법 강의때는 움직이며 (!) 청중과 상호작용하는 듯한 발표방식을 배웠었는데, 리허설 당시에는 프리뷰 화면 없이 저 너머의 누군가를 바라보는 영혼없는 동공으로 눈을 어디다 둬야할지 몰라서   갈곳을 잃었었다. 하지만 발표 당일 현장은 프리뷰 화면도 있고! 유튜브 업로드를 위한 촬영이&amp;nbsp; 진행되었기에, 지정된 구역에서 벗어나면 안되었고, 또 스크린을 쳐다보는 것 보다는 프리뷰 화면을 쳐다보고 발표하는 것을 권장했었다. 발표하는 내내 연습 했던 것 처럼 무의식 적으로 스크린 화면을 쳐다보고 얘기하다가 아차! 싶어서 다시 프리뷰 화면으로 눈을 돌리고를 계속 반복했다. 그래도 결론적으로 발표는 잘 했다고 생각한다! 만족!&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;발표영상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=8sKTvf1c2zQ&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/p&gt;</description>
      <category>devlog/conference</category>
      <category>forward2022</category>
      <category>NHN</category>
      <category>nhnforward</category>
      <category>nhnforward2022</category>
      <category>기술컨퍼런스발표후기</category>
      <category>발표후기</category>
      <category>포워드</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/142</guid>
      <comments>https://yay-dev.tistory.com/entry/NHN-FORWARD-2022-%EB%B0%9C%ED%91%9C-%ED%9B%84%EA%B8%B0#entry142comment</comments>
      <pubDate>Tue, 8 Apr 2025 23:57:19 +0900</pubDate>
    </item>
    <item>
      <title>99클럽 코테 스터디 9일차 TIL</title>
      <link>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9D%BC%EC%B0%A8-TIL</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/3986&quot;&gt;BOJ 3986 - 좋은 단어&lt;/a&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;같은 알파벳끼리 짝을 지어야 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선이 교차하지 않도록&lt;/b&gt;, 즉 &lt;b&gt;스택 구조처럼 처리&lt;/b&gt;해야 함&lt;/li&gt;
&lt;li&gt;모든 문자가 정확히 한 번만 짝지어지면 &amp;rarr; &lt;b&gt;&quot;좋은 단어&quot;&lt;/b&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;스택이 비었거나 top이 다르면 &amp;rarr; &lt;code&gt;append()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;top과 현재 문자가 같으면 &amp;rarr; &lt;code&gt;pop()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단어 순회 후 &lt;b&gt;스택이 비어 있으면&lt;/b&gt; &amp;rarr; 좋은 단어로 카운트&lt;/li&gt;
&lt;li&gt;python에는 peek()이 없으므로, 배열 마지막 요소를 확인하는 것으로 대체&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;  코드&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import sys

n = int(sys.stdin.readline())
count = 0

for _ in range(n):
    word = sys.stdin.readline().strip()
    stack = []
    for c in word:
        if stack and stack[-1] == c:
            stack.pop()
        else:
            stack.append(c)
    if not stack:
        count += 1

print(count)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⏱ 시간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(n &amp;times; m): (n = 단어 수, m = 평균 단어 길이)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  공간 복잡도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(m) (입력 문자열 한 줄씩 처리 &amp;rarr; 리스트 저장 없이 공간 최소화) or O(m * n)&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;짝짓기&lt;/b&gt; 문제랑 매우 밀접함 (ex: 괄호, 알파벳, 연산자 등)&lt;/li&gt;
&lt;li&gt;입력을 한 줄씩 처리하면 &lt;b&gt;불필요한 공간 사용 줄일 수 있음&lt;/b&gt; (백준에서 이런거 되는지 몰랐음)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;str&lt;/code&gt; 같은 내장 타입 이름은 변수명으로 쓰지 않기&lt;/li&gt;
&lt;/ul&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;1902&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ElDPN/btsNdx3ZwNp/emcqSGqpkoCh5OZXV7KrG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ElDPN/btsNdx3ZwNp/emcqSGqpkoCh5OZXV7KrG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ElDPN/btsNdx3ZwNp/emcqSGqpkoCh5OZXV7KrG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FElDPN%2FbtsNdx3ZwNp%2FemcqSGqpkoCh5OZXV7KrG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1902&quot; height=&quot;304&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;304&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;code&gt;O(n &amp;times; m)&lt;/code&gt; + 저장용 리스트로 메모리 조금 더 씀&lt;/li&gt;
&lt;li&gt;개선된 버전은 저장 없이 바로 처리 &amp;rarr; 메모리가 그렇게 크게..줄지는 않음!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>devlog/TIL</category>
      <category>99클럽</category>
      <category>til</category>
      <category>개발자취업</category>
      <category>백준3986</category>
      <category>좋은단어</category>
      <category>코딩테스트준비</category>
      <category>항해99</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/173</guid>
      <comments>https://yay-dev.tistory.com/entry/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9D%BC%EC%B0%A8-TIL#entry173comment</comments>
      <pubDate>Tue, 8 Apr 2025 23:48:48 +0900</pubDate>
    </item>
    <item>
      <title>[오브젝트] 01. 객체 설계</title>
      <link>https://yay-dev.tistory.com/entry/%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-01-%EA%B0%9D%EC%B2%B4-%EC%84%A4%EA%B3%84</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt; &amp;nbsp; 책에서 기억하고 싶은 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절차지향에서의 객체는 수동적이며, 그저 데이터를 담는 존재일 뿐이다. 하지만 객체지향에서는 객체를 생명과 지능을 가진 &lt;b&gt;능동적인 존재&lt;/b&gt;로 설계해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;260&quot; data-start=&quot;174&quot; data-ke-size=&quot;size16&quot;&gt;이 책은 단순히 &quot;객체지향이란 무엇인가?&quot; 에서 멈추지 않고, &lt;b&gt;&quot;어떻게 하면 좋은 객체지향 설계를 할 수 있을까?&quot;&lt;/b&gt; 라는 고민을 해결해준다.&lt;/p&gt;
&lt;p data-end=&quot;390&quot; data-start=&quot;262&quot; data-ke-size=&quot;size16&quot;&gt;훌륭한 객체지향 설계란 &lt;b&gt;객체 간의 협력을 고려하면서, 의존성을 적절히 관리하는 설계&lt;/b&gt;다.&lt;br /&gt;어떤 설계든 정답이 존재하는 것은 아니지만, &lt;b&gt;객체의 자율성을 높이고 불필요한 의존성을 줄이는 것&lt;/b&gt;이 좋은 설계로 이어진다.&lt;/p&gt;
&lt;p data-end=&quot;390&quot; data-start=&quot;262&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;434&quot; data-start=&quot;392&quot; data-ke-size=&quot;size16&quot;&gt;&quot;설계는 코드를 배치하는 것이다.&quot; 이 문장이 특히 인상적이었다.&lt;/p&gt;
&lt;p data-end=&quot;541&quot; data-start=&quot;436&quot; data-ke-size=&quot;size16&quot;&gt;절차지향에서는 &lt;b&gt;데이터와 절차가 분리&lt;/b&gt;되어 있지만, 객체지향에서는 &lt;b&gt;데이터와 절차가 하나의 모듈 안에 공존&lt;/b&gt;한다.&lt;br /&gt;즉, 코드를 어떻게 배치하느냐에 따라 설계 방식이 결정된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅&amp;nbsp; 3줄 요약&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자신의 일은 스스로 하자! 정신을 가진 객체를 개발하자&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&quot;설계는 코드를 배치하는 것이다&quot;&lt;/li&gt;
&lt;li&gt;좋은 설계는 오늘 요구하는 기능을 온전히 수행하면서, 내일의 변경을 매끄럽게 수용할 수 있는 설계다&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BE/BE</category>
      <category>객체지향</category>
      <category>오브젝트</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/172</guid>
      <comments>https://yay-dev.tistory.com/entry/%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-01-%EA%B0%9D%EC%B2%B4-%EC%84%A4%EA%B3%84#entry172comment</comments>
      <pubDate>Sun, 16 Mar 2025 23:25:02 +0900</pubDate>
    </item>
    <item>
      <title>  Docker Desktop for Mac + JDK 17이 실행되지 않는 이유 deep dive!</title>
      <link>https://yay-dev.tistory.com/entry/%F0%9F%90%B3-Docker-Desktop-for-Mac-JDK-17%EC%9D%B4-%EC%8B%A4%ED%96%89%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0-deep-dive</link>
      <description>&lt;p&gt;최근 Docker Desktop for Mac 환경에서 JDK 17을 사용하는 Spring Boot 애플리케이션이 실행되지 않는 문제를 발견했다.&lt;/p&gt;
&lt;p&gt;이상한 점은 Windows 환경에서는 정상 실행되지만, Mac에서는 실행 후 바로 종료된다는 것이다.&lt;br&gt;&amp;quot;환경 차이 때문인가?&amp;quot;   하지만 Docker는 OS와 무관하게 동일하게 실행되어야 하는데, 왜 이런 문제가 발생한 걸까?&lt;/p&gt;
&lt;p&gt;정확한 원인을 찾기 위해 Deep Dive 해보았다.&lt;/p&gt;
&lt;h2&gt;  문제상황&lt;/h2&gt;
&lt;p&gt;Mac에서 실행한 Docker Container의 로그를 확인한 결과,&lt;br&gt;JVM이 컨테이너 내부 리소스를 감지하는 과정에서 NullPointerException 발생했다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;java.lang.NullPointerException: Cannot invoke &amp;quot;jdk.internal.platform.CgroupInfo.getMountPoint()&amp;quot; because &amp;quot;anyController&amp;quot; is null&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;quot;왜 NPE가 발생하는 거지?&amp;quot;  &lt;br&gt;&amp;quot;설마 Docker 컨테이너에 메모리 제한이 걸려있나?&amp;quot;&lt;br&gt;&amp;quot;JVM 설정이 뭔가 잘못됐나?&amp;quot;&lt;br&gt;여러 의문이 들었고, 하나씩 검증해보기로 했다.&lt;/p&gt;
&lt;h2&gt;  Docker container 메모리 제한 확인&lt;/h2&gt;
&lt;p&gt;Docker 컨테이너는 실행할 때 각 컨테이너별로 자원 할당을 설정할 수 있다.&lt;br&gt;혹시 이 설정이 적용된 건 아닌지 확인하기 위해 아래 명령어를 실행했다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;docker exec -it &amp;lt;container_id&amp;gt; java -XX:+PrintFlagsFinal | grep -iE &amp;#39;heap|memory&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행결과: &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;size_t MaxHeapSize                              = 2055208960                                {product} {ergonomic}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;별다른 JVM 설정을 하지 않았는데 heap 메모리가 제한된 것처럼 보인다.&lt;br&gt;Docker 컨테이너 자체에 리소스 제한이 있는지 추가로 확인해보았다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;docker inspect &amp;lt;container_id&amp;gt; | grep -i memory&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행결과:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;&amp;quot;Memory&amp;quot;: 0,
&amp;quot;MemoryReservation&amp;quot;: 0,
&amp;quot;MemorySwap&amp;quot;: 0,
&amp;quot;MemorySwappiness&amp;quot;: null,&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;code&gt;“Memory”: 0&lt;/code&gt; 값은 Docker container에 별다른 리소스 제한이 설정되지 않았다는 의미다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;그런데 JVM에서는 왜 제한이 걸린 것처럼 보일까?  &lt;/p&gt;
&lt;p&gt;검색을 통해 알아보니,&lt;br&gt;Docker Desktop for Mac은 내부적으로 Linux VM을 실행해 동작하며, 이 VM의 cgroup 설정이 JVM이 인식하는 것과 다를 수 있다는 것을 발견했다.&lt;br&gt;이게 정말 문제의 원인일까? 더 깊이 살펴보았다.&lt;/p&gt;
&lt;h2&gt;  Docker Desktop 의 cgroup 확인&lt;/h2&gt;
&lt;p&gt;먼저, JVM이 cgroup 정보를 올바르게 읽을 수 있는지 확인했다.&lt;br&gt;아래 명령어를 실행하면 컨테이너 내부에서 사용 중인 cgroup 버전과 마운트 정보를 확인할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;docker exec -it &amp;lt;container_id&amp;gt; cat /proc/self/mountinfo | grep cgroup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행결과:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;215 214 0:39 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 결과로 볼 때, 컨테이너는 cgroup v2를 사용하고 있다.&lt;br&gt;Docker Desktop이 어떤 cgroup 버전을 사용하는지 추가 확인해보았다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;docker info | grep -i cgroup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행결과: &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Cgroup Driver: cgroupfs
Cgroup Version: 2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기까지 확인했을 때 중요한 점은 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docekr Desktop for Mac은 cgroup v2를 사용&lt;/li&gt;
&lt;li&gt;하지만! Driver는 cgroupfs(cgroup v1스타일) 사용 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그런데...JVM도 cgroup v2, Docker Desktop도 cgroup v2라면...잘 동작해야하는거 아닌가?&lt;br&gt;또 다른 의문이 생겼다.  &lt;/p&gt;
&lt;h2&gt;  JDK 17의 Cgroup 설정 방식&lt;/h2&gt;
&lt;p&gt;이제 글 초반에 언급했던 NPE 에러 로그를 다시 살펴보자.&lt;br&gt;에러가 발생하는 위치를 확장해보면 아래와 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Caused by: java.lang.NullPointerException: Cannot invoke &amp;quot;jdk.internal.platform.CgroupInfo.getMountPoint()&amp;quot; because &amp;quot;anyController&amp;quot; is null
    at java.base/jdk.internal.platform.cgroupv2.CgroupV2Subsystem.getInstance(CgroupV2Subsystem.java:81) ~[na:na]
    at java.base/jdk.internal.platform.CgroupSubsystemFactory.create(CgroupSubsystemFactory.java:113) ~[na:na]
    at java.base/jdk.internal.platform.CgroupMetrics.getInstance(CgroupMetrics.java:167) ~[na:na]
    at java.base/jdk.internal.platform.SystemMetrics.instance(SystemMetrics.java:29) ~[na:na]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 에러가 발생하는 &lt;code&gt;CgroupSubsystemFactory.java&lt;/code&gt; 코드를 보면 원인을 알 수 있다!!!!&lt;br&gt;&lt;code&gt;CgroupSubsystemFactory.java&lt;/code&gt;가 갑자기 왜 나오나?! 라고 생각할 수 있겠지만, &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK 9 버전 이상부터는 &lt;code&gt;UseContainerSupport&lt;/code&gt; 옵션이 기본적으로 활성화된다. &lt;/li&gt;
&lt;li&gt;이 옵션은 JVM이 컨테이너 환경을 감지하고 리소스 제한을 적용하는 기능이다. &lt;/li&gt;
&lt;li&gt;해당 옵션을 제외하지 않는다면, &lt;code&gt;CgroupSubsystemFactory.java&lt;/code&gt; 클래스를 실행하여 리소스 설정을 한다.&lt;br&gt;위의 목적으로 아래 코드들에서는 cgroup과 관련된 설정을 읽어온후 리소스에 대한 설정을 저장한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;에러는 create 메서드에서 시작하지만, 좀 더 중요한 메서드, 코드 위주로 설명하도록 하겠다. &lt;/p&gt;
&lt;h3&gt;determineType() 메서드&lt;/h3&gt;
&lt;p&gt;현재 시스템의 cgroup설정을 파악하고 저장하기 위해서 아래 디렉토리들의 정보를 확인하고, 필요한 정보들을 저장하는 역할을 수행한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Optional&amp;lt;CgroupTypeResult&amp;gt; optResult = null;
try {
    optResult = determineType(&amp;quot;/proc/self/mountinfo&amp;quot;, &amp;quot;/proc/cgroups&amp;quot;, &amp;quot;/proc/self/cgroup&amp;quot;);
}
...
CgroupTypeResult result = optResult.get();
...    
Map&amp;lt;String, CgroupInfo&amp;gt; infos = result.getInfos();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 주어진 디렉토리들은 각각 다음과 같은 역할을 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/proc/cgroups: 시스템에서 사용가능한 cgroup 목록 제공&lt;/li&gt;
&lt;li&gt;/proc/self/mountinfo: 현재 프로세스가 접근할 수 있는 파일시스템, 마운트 정보 제공.(v1인지, v2인지 판단하기 위해 사용)&lt;/li&gt;
&lt;li&gt;/proc/self/cgorup: 현재 프로세스가 속한 cgroup 경로 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서 눈여겨볼 것은 /proc/cgroups 파일을 읽고 컨트롤러 목록을 수집하는 부분이다.&lt;br&gt;아래 코드에서는 사용가능한 cgroup을 수집하고(cpu, memory, blkio 등) infos에 저장한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;final Map&amp;lt;String, CgroupInfo&amp;gt; infos = new HashMap&amp;lt;&amp;gt;();
List&amp;lt;String&amp;gt; lines = CgroupUtil.readAllLinesPrivileged(Paths.get(cgroups));
for (String line : lines) {
    if (line.startsWith(&amp;quot;#&amp;quot;)) {
        continue;
    }
    CgroupInfo info = CgroupInfo.fromCgroupsLine(line);
    switch (info.getName()) {
    case CPU_CTRL:      infos.put(CPU_CTRL, info); break;
    case CPUACCT_CTRL:  infos.put(CPUACCT_CTRL, info); break;
    case CPUSET_CTRL:   infos.put(CPUSET_CTRL, info); break;
    case MEMORY_CTRL:   infos.put(MEMORY_CTRL, info); break;
    case BLKIO_CTRL:    infos.put(BLKIO_CTRL, info); break;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그러나...&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  현재 docker container의 cgroups 정보에는 memory 컨트롤러가 없다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;docker exec -it &amp;lt;container-id&amp;gt; cat /proc/cgroups
#subsys_name    hierarchy    num_cgroups    enabled
cpu    0    21    1
cpuacct    0    21    1
blkio    0    21    1
devices    0    21    1
freezer    0    21    1
net_cls    0    21    1
perf_event    0    21    1
net_prio    0    21    1
hugetlb    0    21    1
pids    0    21    1
rdma    0    21    1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;determineType() 메서드를 수행한 이후 코드를 살펴보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private static final String MEMORY_CTRL = &amp;quot;memory&amp;quot;;
...  
Map&amp;lt;String, CgroupInfo&amp;gt; infos = result.getInfos();
if (result.isCgroupV2()) {
    // For unified it doesn&amp;#39;t matter which controller we pick.
    CgroupInfo anyController = infos.get(MEMORY_CTRL);
    CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(anyController);
    return subsystem != null ? new CgroupMetrics(subsystem) : null;
} else {
    CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance(infos);
    return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;determineType()&lt;/code&gt; 메서드를 통해 설정된 infos를 기준으로 memory에 대한 controller를 조회하지만,&lt;br&gt;infos에는 해당값이 없기 때문에  &lt;strong&gt;CgroupInfo는 null&lt;/strong&gt; 이 된다.&lt;br&gt;이 이후로 별다른 null 처리가 없어서,&lt;br&gt;&lt;code&gt;CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(anyController)&lt;/code&gt;&lt;br&gt;해당 코드에서 &lt;strong&gt;NPE&lt;/strong&gt;가 발생한다. &lt;/p&gt;
&lt;p&gt;결국 Spring Boot 어플리케이션이 실행되지 않고 종료되는 이슈가 발생한다 (or JVM으로 실행되는 어플리케이션 모두). &lt;/p&gt;
&lt;h2&gt;  NPE원인은 찾았지만 진짜진짜진짜진짜최종 원인은?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQfo9x/btsMDa2Y8KJ/4FEk9QPD4Kiw89fYcZ0xB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQfo9x/btsMDa2Y8KJ/4FEk9QPD4Kiw89fYcZ0xB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQfo9x/btsMDa2Y8KJ/4FEk9QPD4Kiw89fYcZ0xB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQfo9x%2FbtsMDa2Y8KJ%2F4FEk9QPD4Kiw89fYcZ0xB1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Docker Desktop git issue에서 발췌해온 내용인데, 현재 JDK 17 + Docker Desktop for Mac 을 사용한다면, 공통적으로 겪고있는 상황이라고 한다. 어플리케이션이 정상적으로 동작하지 않는 원인은 크게 두가지 인데,  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK17에서 memory에 대한 설정이 없는 경우에 대한 Null 처리가 제대로 되어있지 않음 (해당 부분은 픽스됨)&lt;/li&gt;
&lt;li&gt;Docker Desktop for Mac이 사용하는 &lt;code&gt;Linux Kernel 버전 6.12부터 /proc/cgroups 에 memory가 다른 곳으로 이동&lt;/code&gt;함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 모든것이...Linux Kernel의 문제였다니!  &lt;br&gt;Windows에서 정상동작했던 이유는, Docker Desktop이 Hyper-V 기반의 LinuxKit VM을 사용하고, Mac용 Docker Desktop과는 다른 Linux Kernel버전을 사용하고 있어서이다. &lt;/p&gt;
&lt;h2&gt;✅ 어떻게 해결할까?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Docker Desktop for Mac 버전을 다운그레이드 하여 사용한다. 현재 최신버전 이전은 다른 버전의 Linux Kernel을 사용하기 때문에, 현재와 같은 에러가 발생하지 않을 가능성이 높다. &lt;a href=&quot;https://docs.docker.com/desktop/release-notes/#4380&quot;&gt;release note 확인&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Docker Desktop for Mac을 삭제하고, podman, rancher와 같은 다른 대체제로 전환하여 사용한다. (podman에서 실행해보니 정상적으로 잘 동작했음) &lt;/li&gt;
&lt;li&gt;JVM 옵션으로 UseContainerSupport 옵션을 제외시켜서, JVM이 구동될 때, 컨테이너 리소스 제한을 따르지 않도록 설정하면, 문제가 되고있는 클래스 (&lt;strong&gt;CgroupSubsystemFactory.java)&lt;/strong&gt;를 참조하지 않고 바로 호스트의 리소스 설정을 따라 실행된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/docker</category>
      <category>Deepdive</category>
      <category>docker desktop for mac</category>
      <category>docker java 실행안됨</category>
      <category>docker 실행안됨</category>
      <category>jdk17</category>
      <category>linux kernel 6.12</category>
      <category>usecontainersupport</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/171</guid>
      <comments>https://yay-dev.tistory.com/entry/%F0%9F%90%B3-Docker-Desktop-for-Mac-JDK-17%EC%9D%B4-%EC%8B%A4%ED%96%89%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0-deep-dive#entry171comment</comments>
      <pubDate>Thu, 6 Mar 2025 23:45:25 +0900</pubDate>
    </item>
    <item>
      <title>놀고있는 노트북으로 homelab을 꾸려보자-2</title>
      <link>https://yay-dev.tistory.com/entry/TODO-%EB%86%80%EA%B3%A0%EC%9E%88%EB%8A%94-%EB%85%B8%ED%8A%B8%EB%B6%81%EC%9C%BC%EB%A1%9C-homelab%EC%9D%84-%EA%BE%B8%EB%A0%A4%EB%B3%B4%EC%9E%90-2</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_6815.png&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;5712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c02xqN/btsL3no6Rc1/JMbivBKr0mbTIpALX72xvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c02xqN/btsL3no6Rc1/JMbivBKr0mbTIpALX72xvk/img.png&quot; data-alt=&quot;서버랙 (feat. 다이소)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c02xqN/btsL3no6Rc1/JMbivBKr0mbTIpALX72xvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc02xqN%2FbtsL3no6Rc1%2FJMbivBKr0mbTIpALX72xvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;826&quot; height=&quot;1101&quot; data-filename=&quot;IMG_6815.png&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;5712&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버랙 (feat. 다이소)&lt;/figcaption&gt;
&lt;/figure&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;✅ ubuntu-server 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 맥북 --&amp;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;✅ k3s cluster 구성&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ubuntu-server 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북 각각 ubuntu-server를 깔고, docker로 설치완료했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu-server를 깔기 위해서는 ISO 이미지를 다운받아 bootable usb를 만들어야 한다!&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ubuntu-server ISO를 다운받는다&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://etcher.balena.io/&quot;&gt;https://etcher.balena.io/&lt;/a&gt; 를 다운받는다 (ubuntu 오피셜 사이트에서 추천해 준 툴)&lt;/li&gt;
&lt;li&gt;다운받은 ISO 이미지를 선택 -&amp;gt; usb 드라이브 선택 -&amp;gt; usb드라이브에 설치되기까지 대기&lt;/li&gt;
&lt;li&gt;다운이 완료된 usb를 ubuntu-server를 설치하고자 하는 컴퓨터에 연결&lt;/li&gt;
&lt;li&gt;부팅 시 usb로 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여튼 설치 완료했으니, 이제 k3s를 설정하면 된다. 2대밖에 없지만, 나름 control-plane, agent-node 구성으로 세팅할 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;삼성 노트북 - control plane&lt;/li&gt;
&lt;li&gt;hp 노트북 - agent node&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼성 노트북이 디스크 용량이 좀 떨어지지만, hp보다는 cpu성능이 좋아서 control plane으로 설정했다. 둘 다 좋은 성능의 노트북은 아니라서 사실 비교하기도 민망함.. 좀 더 써보고 괜찮다 싶으면 라즈베리 파이를 투입하는 것도 좋을 것 같다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;맥북 -&amp;gt; 홈랩 서버 원격 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 노트북이 오래되기도 하고, 아예 ubuntu-server를 설치했다 보니, 마우스도 없고 폰트 크기설정도 안돼서 (되긴 하지만, 짧게 찾아본 결과 설정이 안 먹었다) 맥북에서 원격 접속해서 이제,, 앞으로는 구린 키감을 느끼며, 잘 안 보여서 눈 찡그리며 타이핑하지 않아도 된다.&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;[ubuntu-server] openssh 설치&lt;/li&gt;
&lt;li&gt;[ubuntu-server] ssh 서비스 활성화, 시작&lt;/li&gt;
&lt;li&gt;[ubuntu-server] ip 확인&lt;/li&gt;
&lt;li&gt;[ubuntu-server] ssh password 접근 활성화&lt;/li&gt;
&lt;li&gt;[mac] ssh 접속&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 Ubuntu-server 쪽에서의 설정만 되면 끝이라고 보면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oepnssh는 아마, ubuntu-server를 설치하면서 같이 설치가 되었을 거라, ssh 서비스가 활성화되어있는지 확인이 필요하다&lt;/p&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;ssh 서비스 활성화, 시작&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;sudo systemctl status ssh // ssh 서비스 상태 확인 
sudo systemctl start ssh // ssh 서비스 시작 
sudo systemctl enable ssh // 자동시작 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 후에 status 명령어를 통해 활성상태를 확인한다. 이렇게 나오면 정상적으로 동작하고 있는 것 `Active: active (running)`&lt;/p&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;ip 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ip addr&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 LAN내 ip주소 확인. 나는 ubuntu-server들을 와이파이에 연결해 뒀으므로 wlan나 wlp로 시작하는 무선랜 인터페이스에서 inet (ipv4) 항목을 참고했다.&lt;/p&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;ssh password 접근 활성화&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;sudo vim /etc/ssh/sshd_config // 선호하는 text editor 사용하기~&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PasswordAuthentication 옵션이 yes로 설정되어 있는지 확인. 주석처리 되어있거나 no로 되어있다면 주석해제 or yes로 변경&lt;/p&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;ssh 접속&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ssh [ubuntu-server 사용자 이름]@[서버IP주소]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 접근설정! 여기에서 접근이 안된다면, 방화벽 설정 때문에 접근이 안될 수 있으니, ubuntu-server에서 ssh를 허용하도록 설정해야 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데, 이렇게 매번 비번 입력하고 들어가기 귀찮으니까...! 스크립트를 추가했다. 나는 서버 두대만 있어서, 이렇게 간단하게 구성했지만, 더 복잡한 구성이 필요하다면..... 그때 수정하는 것으로!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 머신이라 sshpass를 사용해서 비번을 바로 입력하게 하였지만, 회사 컴퓨터, 공용 컴퓨터에서는 보안을 위해서 하지 않는 것을 권장한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

# 서버 정보 설정 
CONTROL1=&quot;IP주소&quot;
AGENT1=&quot;IP주소&quot;
USERNAME= # 서버에 접속할 사용자 이름
PASSWORD= # SSH 접속에 사용할 비밀번호

echo &quot;접속할 서버를 선택하세요:&quot;
echo &quot;1) 서버 1 ($SERVER1)&quot;
echo &quot;2) 서버 2 ($SERVER2)&quot;
echo -n &quot;선택 (1 또는 2): &quot;
read choice

if [ &quot;$choice&quot; = &quot;1&quot; ]; then
    echo &quot;컨트롤-1 ($CONTROL1)에 접속합니다...&quot;
    sshpass -p &quot;$PASSWORD&quot; ssh &quot;$USERNAME@$CONTROL1&quot;
elif [ &quot;$choice&quot; = &quot;2&quot; ]; then
    echo &quot;에이전트-1 ($AGENT1)에 접속합니다...&quot;
    sshpass -p &quot;$PASSWORD&quot; ssh &quot;$USERNAME@$AGENT1&quot;
else
    echo &quot;잘못된 선택입니다. 스크립트를 종료합니다.&quot;
fi&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;홈랩서버 클램쉘 모드 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 원격으로 접속을 하고 나서, 앞으로 이 구린 노트북에서 직접 명령어를 입력할 일은 없을 것! 하고 닫았는데, 연결이 바로 끊겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버처럼 동작하게 하려면, 클램쉘 모드?처럼 동작하도록 설정을 해줘야 한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;sudo vim /etc/systemd/logind.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 내에서 이 항목들을 주석처리 해제 하거나, 수정!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HandleLidSwitch=ignore&lt;/li&gt;
&lt;li&gt;HandleLidSwitchDocked=ignore&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;sudo systemctl restart systemd-logind // 변경사항 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경사항 적용엔 시간이 좀 소요될 수도 있다. 이렇게 하면! 이제 노트북 뚜껑을 닫아도 원격으로 쭈욱 접속할 수 있게 된다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;k3s 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k3s 문서에서 quick start 가이드에 나온 설치는 single-node 기준이라, 내가 원하는 master-agent 구성으로 설정하려면 아래와 같이 설정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;control plane node에서 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;curl -sfL https://get.k3s.io | sh -s - server --cluster-init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;node-token 저장&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;sudo cat /var/lib/rancher/k3s/server/node-token&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;agent node에서 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;curl -sfL https://get.k3s.io | K3S_URL=https://[control-plane IP주소]:6443 K3S_TOKEN=[node-token] sh -&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구성하면!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-02-02_6.56.57.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MfGmX/btsL5QQgqs5/LzK5wS9WPwwKlX7WswWV90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MfGmX/btsL5QQgqs5/LzK5wS9WPwwKlX7WswWV90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MfGmX/btsL5QQgqs5/LzK5wS9WPwwKlX7WswWV90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMfGmX%2FbtsL5QQgqs5%2FLzK5wS9WPwwKlX7WswWV90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;220&quot; data-filename=&quot;2025-02-02_6.56.57.png&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 아름다운 로그를 확인할 수 있다  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 control plane-agent node로 잘 돌아가는지 테스트도 완료했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다음은, 홈 CI/CD 구성해보기 도전해 볼 예정. 하면서 다른 것도 돌아갈만하면 (?) 해볼 예정.&lt;/p&gt;</description>
      <category>DevOps/etc</category>
      <category>homelab</category>
      <category>k3s cluster 구성</category>
      <category>ssh 접속 스크립트</category>
      <category>ubuntu server</category>
      <category>ubuntu server 설치</category>
      <category>ubuntu server 원격접속 설정</category>
      <category>홈랩</category>
      <category>홈서버</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/170</guid>
      <comments>https://yay-dev.tistory.com/entry/TODO-%EB%86%80%EA%B3%A0%EC%9E%88%EB%8A%94-%EB%85%B8%ED%8A%B8%EB%B6%81%EC%9C%BC%EB%A1%9C-homelab%EC%9D%84-%EA%BE%B8%EB%A0%A4%EB%B3%B4%EC%9E%90-2#entry170comment</comments>
      <pubDate>Sat, 1 Feb 2025 23:15:42 +0900</pubDate>
    </item>
    <item>
      <title>놀고있는 노트북으로 homelab을 꾸려보자-1</title>
      <link>https://yay-dev.tistory.com/entry/%EB%86%80%EA%B3%A0%EC%9E%88%EB%8A%94-%EB%85%B8%ED%8A%B8%EB%B6%81%EC%9C%BC%EB%A1%9C-homelab%EC%9D%84-%EA%BE%B8%EB%A0%A4%EB%B3%B4%EC%9E%90-1</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_6814.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boII1p/btsL3mjdSJp/eS29wcI8JE79tGPnqbGKd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boII1p/btsL3mjdSJp/eS29wcI8JE79tGPnqbGKd1/img.png&quot; data-alt=&quot;노트북 진짜 더럽다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boII1p/btsL3mjdSJp/eS29wcI8JE79tGPnqbGKd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboII1p%2FbtsL3mjdSJp%2FeS29wcI8JE79tGPnqbGKd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;IMG_6814.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노트북 진짜 더럽다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게는 놀고 있는 노트북이 2대가 있다. 대학생 때 사용했던 HP 랩탑과 아버지에게 갈취(?) 했던 삼성 노트북.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년부터 사용하지 않는 짐 정리 대 프로젝트를 진행 중인 와중에, 이 두대의 노트북이 정말,, 눈엣가시처럼 사라지지도 않고 계속 거실장에 거치되어 있는 게, 나의 모든 움직임에 &quot;감히 나를 그냥 둬?&quot;라고 말하는 것 같은 느낌을 받았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새해가 되었으니, 새 마음으로 이 노트북을 보내줄까 하다가 보내줄수도 없고 (원하는 사람이 없을 듯), 이전부터 해보고 싶었던 Homelab 구축을 이거로 연습해 볼까?라는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 내 노트북 두대의 스펙은 이렇다&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;삼성 노트북&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;운영체제&lt;/b&gt;: Windows&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU:&lt;/b&gt; Intel i7-3537U&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAM:&lt;/b&gt; 4GB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저장공간:&lt;/b&gt; 256GB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HP 노트북
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;운영체제&lt;/b&gt;: Windows 10 (2016년도 기준)&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU:&lt;/b&gt; Intel Core i3 M 370&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAM:&lt;/b&gt; 6GB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저장공간:&lt;/b&gt; 464GB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HP 노트북의 RAM이 좀 이상하다고 생각이 들텐데, 가난한 대학생때, 뚜따해서 소심하게 늘렸다. 당시에 꽤나 효과를 봤었다. (윈도우를 밀어버리고 ubuntu를 사용하긴 했지만)&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;여튼 나의 조촐하고도, 죽여달라는 비명이 들리는 Homelab 스펙이다. 이 두 노트북을 가지고 앞으로 이렇게 할 거다&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;윈도우를 삭제하고, Ubuntu Server를 깔아준다&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Ubuntu Server에 k3s를 설치한다&lt;/li&gt;
&lt;li&gt;나만의 kubernetes sandbox cluster를 구축한다!&amp;nbsp;&lt;/li&gt;
&lt;/ol&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-02-01-20-34-44.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/7HnBf/btsL5usWoVP/uRSu9nZBGkEcq22E56KXsK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7HnBf/btsL5usWoVP/uRSu9nZBGkEcq22E56KXsK/img.jpg&quot; data-alt=&quot;디스크야 힘을내&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7HnBf/btsL5usWoVP/uRSu9nZBGkEcq22E56KXsK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7HnBf%2FbtsL5usWoVP%2FuRSu9nZBGkEcq22E56KXsK%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;820&quot; height=&quot;1093&quot; data-filename=&quot;KakaoTalk_Photo_2025-02-01-20-34-44.jpeg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;디스크야 힘을내&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게든 되겠지? 하하하!&lt;/p&gt;</description>
      <category>DevOps/etc</category>
      <category>homelab</category>
      <category>K3S</category>
      <category>낡은노트북</category>
      <category>서버</category>
      <category>홈랩</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/169</guid>
      <comments>https://yay-dev.tistory.com/entry/%EB%86%80%EA%B3%A0%EC%9E%88%EB%8A%94-%EB%85%B8%ED%8A%B8%EB%B6%81%EC%9C%BC%EB%A1%9C-homelab%EC%9D%84-%EA%BE%B8%EB%A0%A4%EB%B3%B4%EC%9E%90-1#entry169comment</comments>
      <pubDate>Sat, 1 Feb 2025 22:38:51 +0900</pubDate>
    </item>
    <item>
      <title>[Leetcode] 110. Balanced Binary Tree</title>
      <link>https://yay-dev.tistory.com/entry/Leetcode-110-Balanced-Binary-Tree</link>
      <description>&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;code&gt;DFS&lt;/code&gt;, &lt;code&gt;Binary Tree&lt;/code&gt;, &lt;code&gt;Tree&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;난이도: Easy&lt;/li&gt;
&lt;li&gt;링크: &lt;a href=&quot;https://leetcode.com/problems/balanced-binary-tree/description/&quot;&gt;https://leetcode.com/problems/balanced-binary-tree/description/&lt;/a&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;주어진 Binary Tree가 height balanced 인 상태인지 확인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;height balanced = 두 서브트리의 높이의 차이가 1이상이 아닌 트리&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;노드 개수 범위 [0, 5000]&lt;/li&gt;
&lt;li&gt;10^4 &amp;lt;= node.val &amp;lt;= 10^4 -&amp;gt; &lt;code&gt;O(N) 시간복잡도 이전으로 풀어야 한다&lt;/code&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;높이 차이가 1을 초과하면, 트리는 불균형하다고 판단, return False&lt;/li&gt;
&lt;li&gt;DFS방식으로 각 노드의 서브트리 높이를 계산하며 균형여부를 확인&lt;/li&gt;
&lt;li&gt;Stack으로 DFS를 풀이하니, 각 노드에 방문했는지 여부를 저장하는 visited 배열 필요&lt;/li&gt;
&lt;li&gt;현재 노드의 왼쪽 서브트리, 오른쪽 서브트리의 높이를 확인해야하기 때문에 (재귀의 경우는 반환값으로), heights라는 배열 필요&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;높이 차이를 초과하면, False&lt;/li&gt;
&lt;li&gt;초과하지 않는다면, 현재 왼쪽 서브트리, 오른쪽 서브트리의 높이에 + 1한 값을 현재 노드의 높이로 heights 배열에 저장&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;시간 복잡도: 모든 노드에 한번씩 방문 &lt;code&gt;O(N)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;공간 복잡도: 스택에 저장되는 노드의 최대 개수 = 높이 &lt;code&gt;O(H)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -&amp;gt; bool:
        if not root:
            return True

        stack = [(root, False)]
        heights = {}

        while stack:
            node, visited = stack.pop()

            if not node:
                continue

            if visited:
                leftHeight = heights.get(node.left, 0)
                rightHeight = heights.get(node.right, 0)

                if abs(leftHeight - rightHeight) &amp;gt; 1:
                    return False

                heights[node] = max(leftHeight, rightHeight) + 1
            else:
                stack.append((node, True))
                stack.append((node.left, False))
                stack.append((node.right, False))
        return True&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>balanced binarytree</category>
      <category>binaryTree</category>
      <category>DFS</category>
      <category>LeetCode</category>
      <category>leetcode 110</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/164</guid>
      <comments>https://yay-dev.tistory.com/entry/Leetcode-110-Balanced-Binary-Tree#entry164comment</comments>
      <pubDate>Fri, 22 Nov 2024 09:25:05 +0900</pubDate>
    </item>
    <item>
      <title>Eureka와 Eureka 설정방법</title>
      <link>https://yay-dev.tistory.com/entry/Eureka%EC%99%80-Eureka-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h1&gt;Eureka 란?&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Netflix가 개발한 &lt;code&gt;서비스 디스커버리&lt;/code&gt; 툴&lt;/li&gt;
&lt;li&gt;MSA 아키텍쳐에서 각 인스턴스의 위치를 자동으로 찾고, 관리&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;MSA에서 중요한 컴포넌트 &amp;rarr; monolithic 에서는 서로의 인스턴스를 찾을 필요가 없었으니까!&lt;/li&gt;
&lt;li&gt;여러 서비스 인스턴스와 그 위치를 자동으로 탐지하고 관리하는 기능 &amp;rarr; 분리되어있는 MSA 환경에서의 개발은 복합적인 MSA의 호출 발생&lt;/li&gt;
&lt;li&gt;서비스가 서로를 찾고 통신할 수 있도록 해주며, 수동으로 서비스 위치를 관리하는 &lt;code&gt;복잡성을 낮춤&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Eureka의 구성요소&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ0dJk/btsKPgjamQR/rjb9geXxWm8e7qbUg1mfA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ0dJk/btsKPgjamQR/rjb9geXxWm8e7qbUg1mfA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ0dJk/btsKPgjamQR/rjb9geXxWm8e7qbUg1mfA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ0dJk%2FbtsKPgjamQR%2Frjb9geXxWm8e7qbUg1mfA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;382&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eureka Server&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;li&gt;모든 클라이언트가 서버에 등록을 해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eureka Client&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;각 MSA 서비스&lt;/li&gt;
&lt;li&gt;E.g. product msa, order msa&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;code&gt;서비스 등록&lt;/code&gt;과 &lt;code&gt;발견&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;하트비트&lt;/code&gt; 매커니즘
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트(=msa)는 정해진 간격으로 Eureka 서버에 하트비트 신호를 보내서 &lt;code&gt;&quot;아직 잘 살아있어!&quot;&lt;/code&gt; 라고 알림. 이를 통해 Eureka 서버가 서비스의 &lt;code&gt;가용성&lt;/code&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;네트워크 문제 등의 이유로 다수의 클라이언트가 동시에 서버와의 연결이 끊어졌을 때, Eureka 서버는 이를 장애로 간주하지 않고 일시적인 문제로 판단 후 자동으로 가용 서비스 목록을 유지.&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;code&gt;서버 가용성&lt;/code&gt;과 &lt;code&gt;부하분산&lt;/code&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;code&gt;@LoadBalanced&lt;/code&gt; -&amp;gt; 다중 서버환경에서 하나의 서버로만 요청을 보내지 않고 골고루 분산해 주는 방식.&lt;/li&gt;
&lt;li&gt;Eureka 단독으로 사용했을 시, 내부적으로 Spring cloud Load balancer를 사용하여 동작함&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;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;+----------------+                  +-------------------+
|                |   1. 등록         |                   |
|  Eureka Client +-----------------&amp;gt;   Eureka Server    | 
| = MSA service  |                  |                   |
|                |                  |                   |
+--------+-------+                  +---------+---------+
         |                                      |
         |                                      |
         |   2. 주기적으로 하트비트 전송              |
         |-------------------------------------&amp;gt;|
         |                                      |
         |                                      |
         |   3. 레지스트리 정보 가져오기  |
         |&amp;lt;-------------------------------------|
         |                                      |
         +                                      +&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서비스 A가 시작될 때, 서비스 인스턴스 정보 (IP주소, 포트, 상태 등)을 Eureka Server에 &lt;code&gt;등록&lt;/code&gt; 요청을 보냄. 이 정보는 &lt;code&gt;eureka server registry&lt;/code&gt;에 저장&lt;/li&gt;
&lt;li&gt;서비스 인스턴스는 설정된 시간마다 (default 30초) Eureka Server에 하트비트를 보냄. 하트비트를 통해 server는 client의 가용성을 파악하고, 어떤 인스턴스가 여전히 활성 상태인지 판단&lt;/li&gt;
&lt;li&gt;MSA 서비스 A가 다른 서비스 B와 통신을 시작하기 전에 Eureka server에 MSA 서비스 B의 위치 정보를 요청! 서버는 이 요청을 처리하고, B의 최신정보를 A에 반환&lt;/li&gt;
&lt;/ol&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;1504&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn5Ni1/btsKPPFlwqw/slkzn9Ois3xt8CShi7NQKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn5Ni1/btsKPPFlwqw/slkzn9Ois3xt8CShi7NQKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn5Ni1/btsKPPFlwqw/slkzn9Ois3xt8CShi7NQKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn5Ni1%2FbtsKPPFlwqw%2Fslkzn9Ois3xt8CShi7NQKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1504&quot; height=&quot;912&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eureka- &lt;code&gt;Server&lt;/code&gt; 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;build.gradle&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Application.java&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@SpringBootApplication 
@EnableEurekaServer 
public class EurekaServerApplication {
 public static void main(String[] args) {
  SpringApplication.run(EurekaServerApplication.class, args); 
  } 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml (단일)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;server:
  port: 8761

eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
  server:
    enableSelfPreservation: false  # 네트워크 문제 시 서버의 자가 보호 모드 비활성화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml (클러스터링)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;server:
  port: 8761

eureka:
  client:
    registerWithEureka: true  # Eureka 서버끼리 통신 하기 위해
    fetchRegistry: true
    defaultZone: http://eureka-server1.com:8761/eureka/,http://eureka-server2.com:8761/eureka/,http://eureka-server3.com:8761/eureka/
  server:
    enableSelfPreservation: false  # 네트워크 문제 시 서버의 자가 보호 모드 비활성화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eureka- &lt;code&gt;Client&lt;/code&gt; 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;build.gradle&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Application.java&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml (단일)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml (클러스터링)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://eureka-server1.com:8761/eureka/,http://eureka-server2.com:8761/eureka/,http://eureka-server3.com:8761/eureka/&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BE/BE</category>
      <category>EUREKA</category>
      <category>eureka 설정</category>
      <category>microservice architecture</category>
      <category>MSA</category>
      <category>Service discovery</category>
      <category>서비스디스커버리</category>
      <category>오블완</category>
      <category>유레카</category>
      <category>티스토리챌린지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/162</guid>
      <comments>https://yay-dev.tistory.com/entry/Eureka%EC%99%80-Eureka-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95#entry162comment</comments>
      <pubDate>Wed, 20 Nov 2024 10:40:21 +0900</pubDate>
    </item>
    <item>
      <title>[Leetcode] 121. Best Time to Buy and Sell Stock</title>
      <link>https://yay-dev.tistory.com/entry/Leetcode-121-Best-Time-to-Buy-and-Sell-Stock</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;문제: Best&amp;nbsp;Time&amp;nbsp;to&amp;nbsp;Buy&amp;nbsp;and&amp;nbsp;Sell&amp;nbsp;Stock&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;관련 토픽: Array, Dynamic Programming&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;난이도: Easy&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;링크 &lt;a href=&quot;https://leetcode.com/problems/best-time-to-buy-and-sell-stock&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/best-time-to-buy-and-sell-stock&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; 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;prices 배열은 i번째 날짜의 주식의 가격을 담음&lt;/li&gt;
&lt;li&gt;정해진 하루에 주식을 사고, 주식을 산 시점 이후에 주식을 판매&lt;/li&gt;
&lt;li&gt;주식을 구매, 판매하며 어떤 조합이 가장 큰 수익을 낼 수 있는지 탐색&lt;/li&gt;
&lt;li&gt;수익이 발생하지 않는 경우에는 0을 반환&amp;nbsp;&amp;nbsp;&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;1 &amp;lt;= prices.length &amp;lt;= 10^5 (10만개)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10만개의 요소를 다루는 문제는 효율적으로 풀이 하는 것이 좋다&amp;nbsp;&lt;/li&gt;
&lt;li&gt;O(n) ~ O(nlogn) 시간 복잡도의 풀이가 필요!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0 &amp;lt;= pricese[i] &amp;lt;= 10^4&lt;br /&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;h3 data-ke-size=&quot;size23&quot;&gt;풀이&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 태그에는 Dynamic Programming으로 나와있긴 하지만, 여기서의 해법은 두가지의 포인터를 가지고 문제를 해결했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결할때 접근한 방식은:&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지금 이 가격이 내가 지금까지 봐온 가격중에 가장 싼가? (주식 구매를 위해)&lt;/li&gt;
&lt;li&gt;지금 이 가격으로 판매를 하면 지금까지 확인한 최대 이율보다 더 좋은 수익을 발생 시킬 수 있나?&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두개의 기준을 가지고 문제를 풀이했다. Two pointer + Greedy 가 섞인 해법이다.&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. prices = [7,1,5,3,6,4] 라고 했을 때, buy와 sell이라는 포인터를 추가한다&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_ECD93F415DDF-1.jpeg&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QA7RO/btsKIziv5MY/IgDiYDFKiFteBw36cjkMJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QA7RO/btsKIziv5MY/IgDiYDFKiFteBw36cjkMJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QA7RO/btsKIziv5MY/IgDiYDFKiFteBw36cjkMJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQA7RO%2FbtsKIziv5MY%2FIgDiYDFKiFteBw36cjkMJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1292&quot; height=&quot;675&quot; data-filename=&quot;edited_IMG_ECD93F415DDF-1.jpeg&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;675&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;2. prices 배열을 순회하면서, 가장 싼 가격과 가장 비싼 수익을 낼 수 있는 것을 찾는다. 가장 싼 가격을 찾았을 때는, buy를 sell (주로 탐색하는 포인터)로 변경을 한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_0040.PNG&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nCrrH/btsKICzuNPr/zkB3A0qsQjVaUxNPPMrimk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nCrrH/btsKICzuNPr/zkB3A0qsQjVaUxNPPMrimk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nCrrH/btsKICzuNPr/zkB3A0qsQjVaUxNPPMrimk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnCrrH%2FbtsKICzuNPr%2FzkB3A0qsQjVaUxNPPMrimk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1526&quot; height=&quot;939&quot; data-filename=&quot;edited_IMG_0040.PNG&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;939&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. sell의 값을 기준으로 현재 수익을 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_0041.PNG&quot; data-origin-width=&quot;1591&quot; data-origin-height=&quot;935&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOIZd0/btsKGRSstVC/YZAPBTstc1blr81FGiEYz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOIZd0/btsKGRSstVC/YZAPBTstc1blr81FGiEYz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOIZd0/btsKGRSstVC/YZAPBTstc1blr81FGiEYz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOIZd0%2FbtsKGRSstVC%2FYZAPBTstc1blr81FGiEYz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1591&quot; height=&quot;935&quot; data-filename=&quot;edited_IMG_0041.PNG&quot; data-origin-width=&quot;1591&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;4. 수익이 현재 최고수익 변수인 maxProfit 값보다 큰지 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_0042.PNG&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;903&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Km8eo/btsKIuhoxu9/5GkkGsUFGfOiQIoNPodKD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Km8eo/btsKIuhoxu9/5GkkGsUFGfOiQIoNPodKD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Km8eo/btsKIuhoxu9/5GkkGsUFGfOiQIoNPodKD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKm8eo%2FbtsKIuhoxu9%2F5GkkGsUFGfOiQIoNPodKD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1542&quot; height=&quot;903&quot; data-filename=&quot;edited_IMG_0042.PNG&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;903&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;5. 현재 수익이 maxProfit보다 더 큰 경우, maxProfit의 값을 변경한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_0043.PNG&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TQkiB/btsKHaLgcLx/31XJcQdHUdOJtEKyXwua30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TQkiB/btsKHaLgcLx/31XJcQdHUdOJtEKyXwua30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TQkiB/btsKHaLgcLx/31XJcQdHUdOJtEKyXwua30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTQkiB%2FbtsKHaLgcLx%2F31XJcQdHUdOJtEKyXwua30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1706&quot; height=&quot;908&quot; data-filename=&quot;edited_IMG_0043.PNG&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 남은 요소들중에 더 큰 수익을 낼 수 있는지, sell은 배열의 끝까지 탐색한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_0044.PNG&quot; data-origin-width=&quot;1597&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q7B2Z/btsKI2Eu1TT/gSJIcHyLjQOYKiJk3UIqek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q7B2Z/btsKI2Eu1TT/gSJIcHyLjQOYKiJk3UIqek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q7B2Z/btsKI2Eu1TT/gSJIcHyLjQOYKiJk3UIqek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ7B2Z%2FbtsKI2Eu1TT%2FgSJIcHyLjQOYKiJk3UIqek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1597&quot; height=&quot;869&quot; data-filename=&quot;edited_IMG_0044.PNG&quot; data-origin-width=&quot;1597&quot; data-origin-height=&quot;869&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;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1731510177457&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def maxProfit(self, prices: List[int]) -&amp;gt; int:
        buy, sell = 0, 1
        maxProfit = 0

        while sell &amp;lt; len(prices):
            if prices[buy] &amp;lt; prices[sell]:
                maxProfit = max(maxProfit, prices[sell] - prices[buy])
            else:
                buy = sell 
            sell += 1 

        return maxProfit&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간복잡도, 공간 복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 복잡도: O(n)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값의 길이에 따라 루프를 탐색 하므로 O(n)&lt;/li&gt;
&lt;li&gt;sell은 prices의 모든 요소에 대해 한 번씩만 탐색!&amp;nbsp; n번 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간 복잡도: O(1)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값인 prices를 제외하고, maxProfit, buy, sell 변수만 사용해서 공간복잡도는 상수&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>array</category>
      <category>LeetCode</category>
      <category>leetcode 121</category>
      <category>two pointers</category>
      <category>릿코드</category>
      <category>알고리즘</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/160</guid>
      <comments>https://yay-dev.tistory.com/entry/Leetcode-121-Best-Time-to-Buy-and-Sell-Stock#entry160comment</comments>
      <pubDate>Thu, 14 Nov 2024 00:07:13 +0900</pubDate>
    </item>
    <item>
      <title>[Leetcode] 21. Merge Two Sorted Lists + 그림 풀이</title>
      <link>https://yay-dev.tistory.com/entry/Leetcode-Merge-Two-Sorted-Lists-%EA%B7%B8%EB%A6%BC-%ED%92%80%EC%9D%B4</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제: Merge&amp;nbsp;Two&amp;nbsp;Sorted&amp;nbsp;Lists&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;관련 토픽: LinkedList, Recursion&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;난이도: Easy&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;링크 &amp;nbsp;&lt;/span&gt;&lt;a style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; href=&quot;https://leetcode.com/problems/merge-two-sorted-lists/description/&quot;&gt;https://leetcode.com/problems/merge-two-sorted-lists&lt;/a&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;list1, list2 LinkedList를 정렬된 하나의 LinkedList로 합치기&amp;nbsp;&lt;/li&gt;
&lt;li&gt;list1, list2는 정렬된 LinkedList&amp;nbsp;&lt;/li&gt;
&lt;li&gt;return 값의 list는 두 리스트를 기반으로 만든 값이어야 함&amp;nbsp;&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;LinkedList의 노드 길이는 [0, 50]&lt;/li&gt;
&lt;li&gt;-100 &amp;lt;= node.val &amp;lt;= 100&amp;nbsp;&lt;/li&gt;
&lt;li&gt;list1, list2는 오름차순 정렬 (non-decreasing)&amp;nbsp;&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;이미 정렬된 리스트들을 하나로 합치기 위해서는 brute-forcing, LinkedList, recursion을 사용할 수 도 있음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서는 LinkedList를 사용해서 풀이&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. list1과 list2를 합치기 위해서, dummy linklist node를 하나 추가! list1과 list2의 앞에 node 하나를 덧붙인다는 생각.&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_0033.PNG&quot; data-origin-width=&quot;1849&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LkWrz/btsKHweEs0d/bgsMxJVsL8AMhnL1KJTVZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LkWrz/btsKHweEs0d/bgsMxJVsL8AMhnL1KJTVZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LkWrz/btsKHweEs0d/bgsMxJVsL8AMhnL1KJTVZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLkWrz%2FbtsKHweEs0d%2FbgsMxJVsL8AMhnL1KJTVZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1849&quot; height=&quot;864&quot; data-filename=&quot;IMG_0033.PNG&quot; data-origin-width=&quot;1849&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. list1, list2를 함께 iterate 하면서 값을 비교하고 dummy node에 연결해 나아간다&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_0034.PNG&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1001&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UTl57/btsKFl0vanx/F8klikyTJFrS9sUiKumVW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UTl57/btsKFl0vanx/F8klikyTJFrS9sUiKumVW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UTl57/btsKFl0vanx/F8klikyTJFrS9sUiKumVW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUTl57%2FbtsKFl0vanx%2FF8klikyTJFrS9sUiKumVW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1668&quot; height=&quot;1001&quot; data-filename=&quot;IMG_0034.PNG&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1001&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. 현재 list1, list2가 각각 가리키는 값을 비교하고, 오름차순 정렬에 맞는 값으로 dummy의 next 포인터를 연결해 준다&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_0037.PNG&quot; data-origin-width=&quot;1849&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpziwK/btsKGqGu1c0/HTR2X9EKai8ahrNTkoBPi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpziwK/btsKGqGu1c0/HTR2X9EKai8ahrNTkoBPi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpziwK/btsKGqGu1c0/HTR2X9EKai8ahrNTkoBPi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpziwK%2FbtsKGqGu1c0%2FHTR2X9EKai8ahrNTkoBPi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1849&quot; height=&quot;828&quot; data-filename=&quot;IMG_0037.PNG&quot; data-origin-width=&quot;1849&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 길이가 다른 list의 경우에는, 먼저 list의 끝 (=None)을 마주하는 시점에 loop를 종료한다&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_0036.PNG&quot; data-origin-width=&quot;1779&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxwBxn/btsKHdsSaR5/t9oeGOsTlI3g1HqkTfvBgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxwBxn/btsKHdsSaR5/t9oeGOsTlI3g1HqkTfvBgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxwBxn/btsKHdsSaR5/t9oeGOsTlI3g1HqkTfvBgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxwBxn%2FbtsKHdsSaR5%2Ft9oeGOsTlI3g1HqkTfvBgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1779&quot; height=&quot;869&quot; data-filename=&quot;IMG_0036.PNG&quot; data-origin-width=&quot;1779&quot; data-origin-height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-1. Loop가 종료된 시점에서 None을 가르키지 않는 list의 나머지 부분과 dummy node를 이어준다&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_A83F46BD1ED7-1.jpeg&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfy9ow/btsKGIUsefh/evLfyox78SwpXGQ6SzeJQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfy9ow/btsKGIUsefh/evLfyox78SwpXGQ6SzeJQ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfy9ow/btsKGIUsefh/evLfyox78SwpXGQ6SzeJQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfy9ow%2FbtsKGIUsefh%2FevLfyox78SwpXGQ6SzeJQ1%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;1554&quot; height=&quot;852&quot; data-filename=&quot;IMG_A83F46BD1ED7-1.jpeg&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1731423624193&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -&amp;gt; Optional[ListNode]:
        dummy = ListNode(0)
        current = dummy 

        while list1 is not None and list2 is not None: 
            if list1.val &amp;lt; list2.val: 
                current.next = list1
                list1 = list1.next
            else:
                current.next = list2
                list2 = list2.next
            current = current.next

        if list1 is not None: 
            current.next = list1
        else:
            current.next = list2

        return dummy.next&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간, 공간 복잡도&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 복잡도 O(n+m)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 리스트를 병합하려면 각 리스트를 독립적으로 iterate 해야 함&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시에 같은 pointer가 가르키는 곳을 순회하는 것이 아닌, 조건 (list1.val &amp;lt; list2.val)에 부합하는 경우만 다음 val로 이동&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;list1의 길이가 n, list2의 길이가 m이라면 n + m의 반복이 발생&lt;/li&gt;
&lt;li&gt;W.C는 모든 노드를 하나씩 비교해 가면서 순회 -&amp;gt; n + m 의 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간 복잡도 O(1)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dummy, current와 같은 포인터 생성&lt;/li&gt;
&lt;li&gt;dummy = ListNode(0)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;둘다 상수 공간 복잡도에 해당 (이유: 입력크기 O(n+m)과 상관없이 항상 하나의 노드만 사용하기 때문)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>LeetCode</category>
      <category>leetcode 21</category>
      <category>LinkedList</category>
      <category>Merge Two Sorted Lists</category>
      <category>릿코드</category>
      <category>알고리즘</category>
      <category>알고리즘문제풀이</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/159</guid>
      <comments>https://yay-dev.tistory.com/entry/Leetcode-Merge-Two-Sorted-Lists-%EA%B7%B8%EB%A6%BC-%ED%92%80%EC%9D%B4#entry159comment</comments>
      <pubDate>Wed, 13 Nov 2024 00:05:54 +0900</pubDate>
    </item>
    <item>
      <title>장비병 고치기 위한 온몸 비틀기 - 맥북 m1 에어 듀얼모니터 설정 (with LG 듀얼업)</title>
      <link>https://yay-dev.tistory.com/entry/%EC%9E%A5%EB%B9%84%EB%B3%91-%EA%B3%A0%EC%B9%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%98%A8%EB%AA%B8-%EB%B9%84%ED%8B%80%EA%B8%B0-%EB%A7%A5%EB%B6%81-m1-%EC%97%90%EC%96%B4-%EB%93%80%EC%96%BC%EB%AA%A8%EB%8B%88%ED%84%B0-%EC%84%A4%EC%A0%95-with-LG-%EB%93%80%EC%96%BC%EC%97%85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나에게는 오래된 병이 있다. 지병인데, 모든 분야에 나타나는 고칠 수 없는 병이다. &quot;장비병&quot;이라고,,,불치병을 앓고(?) 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라, 키보드, 그리고 최근에는 캠핑용품으로 뻗어나가는 전적이 있는데, 오래전 부터 애플 장비도 여러 것들을 거쳐왔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴사를 하게 되면서, 잘 쓰던 16인치 맥북 프로를 보내고 개인용 노트북이 필요해져서 당시 따끈따끈한 m1 칩을 탑재한 맥북에어를 구매했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 개발, 유튜브, 웹 서칭 등등 모든 목적을 위한 노트북 구매였고, 당시에는 원모니터 또는 맥북 하나로만 개발하는 것에 맛 들렸어서 (이상한 미니멀리즘에 취했었음) `m1 에어는 하나의 디스플레이만 지원한다` 는 사실도 괜찮았다.&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;이전에 눈여겨 봤던 LG 듀얼업 모니터를 서브 모니터로 쓸 생각으로 구매했고! 이걸 이제 연결해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맥북 m1 에어 또는 그 후 모델중에 하나의 모니터만 디스플레이를 지원하는 경우, 필요한 물건은 DisplayLink를 지원하는 어댑터!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어댑터의 종류는 대표적으로 여러개가 있는데,&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;벨킨 도킹 스테이션&lt;/li&gt;
&lt;li&gt;넥시 HDMI 컨버터&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Dell 도킹 스테이션&lt;/li&gt;
&lt;li&gt;플러거블 (Amazon) 어댑터&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등 다양하다. 가격대도 다양한데, 나는 우선 가장 저렴하게 설정해 볼 수 있는 넥시 HDMI로 테스트해보기로 했다.&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_4773.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVFJde/btsKF7zbMP8/gMmSANrkgZ56krhiefHa70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVFJde/btsKF7zbMP8/gMmSANrkgZ56krhiefHa70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVFJde/btsKF7zbMP8/gMmSANrkgZ56krhiefHa70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVFJde%2FbtsKF7zbMP8%2FgMmSANrkgZ56krhiefHa70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;608&quot; data-filename=&quot;IMG_4773.png&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;쿠팡에서 주문했고 링크는 &lt;a href=&quot;https://www.coupang.com/vp/products/2174831738?vendorItemId=71685921858&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731378620010&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;넥시 USB 3.0 to HDMI 컨버터 - 변환젠더 | 쿠팡&quot; data-og-description=&quot;현재 별점 4.8점, 리뷰 354개를 가진 넥시 USB 3.0 to HDMI 컨버터! 지금 쿠팡에서 더 저렴하고 다양한 변환젠더 제품들을 확인해보세요.&quot; data-og-host=&quot;www.coupang.com&quot; data-og-source-url=&quot;https://www.coupang.com/vp/products/2174831738?vendorItemId=71685921858&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; data-og-url=&quot;https://www.coupang.com/vp/products/2174831738?vendorItemId=71685921858&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/keHKj/hyXwtpXQc7/oqVVuyCBL3lW1zVQK53G91/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/H2kBA/hyXwvagMre/QrTk00IvWeDKJ9R6fWGFB1/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492&quot;&gt;&lt;a href=&quot;https://www.coupang.com/vp/products/2174831738?vendorItemId=71685921858&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.coupang.com/vp/products/2174831738?vendorItemId=71685921858&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/keHKj/hyXwtpXQc7/oqVVuyCBL3lW1zVQK53G91/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/H2kBA/hyXwvagMre/QrTk00IvWeDKJ9R6fWGFB1/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492');&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;넥시 USB 3.0 to HDMI 컨버터 - 변환젠더 | 쿠팡&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 별점 4.8점, 리뷰 354개를 가진 넥시 USB 3.0 to HDMI 컨버터! 지금 쿠팡에서 더 저렴하고 다양한 변환젠더 제품들을 확인해보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.coupang.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설치 및 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치는 쉽다. 모니터에 Hdmi 선을 연결하고, usb A를 맥북 어댑터에 연결하면 끝!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정은 DisplayLink 프로그램을 다운받야아 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731381984912&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;DisplayLink macOS Software for Downloads Section | Synaptics&quot; data-og-description=&quot;DisplayLink macOS Software DisplayLink Manager is a new way to enable your DisplayLink dock, adapter or monitor on macOS platforms. It's an application that combines our latest driver with features that streamline the setup of mutliple displays up to 4K.&quot; data-og-host=&quot;www.synaptics.com&quot; data-og-source-url=&quot;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&quot; data-og-url=&quot;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eGajv/hyXwrMsWIT/jBxS5Zt5UhXzZEleHgjZBK/img.png?width=2560&amp;amp;height=1024&amp;amp;face=0_0_2560_1024,https://scrap.kakaocdn.net/dn/cQryRm/hyXwiPxz0N/XqprxGtgtk12YhsfqdMPI1/img.png?width=1388&amp;amp;height=292&amp;amp;face=0_0_1388_292&quot;&gt;&lt;a href=&quot;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.synaptics.com/products/displaylink-graphics/downloads/macos&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eGajv/hyXwrMsWIT/jBxS5Zt5UhXzZEleHgjZBK/img.png?width=2560&amp;amp;height=1024&amp;amp;face=0_0_2560_1024,https://scrap.kakaocdn.net/dn/cQryRm/hyXwiPxz0N/XqprxGtgtk12YhsfqdMPI1/img.png?width=1388&amp;amp;height=292&amp;amp;face=0_0_1388_292');&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;DisplayLink macOS Software for Downloads Section | Synaptics&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DisplayLink macOS Software DisplayLink Manager is a new way to enable your DisplayLink dock, adapter or monitor on macOS platforms. It's an application that combines our latest driver with features that streamline the setup of mutliple displays up to 4K.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.synaptics.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 다운받고 권한 설정을 해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-12 오후 12.30.16.png&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;1480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kBK28/btsKEvO5aOw/FchrNDEkNQP20KjYZJEeiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kBK28/btsKEvO5aOw/FchrNDEkNQP20KjYZJEeiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kBK28/btsKEvO5aOw/FchrNDEkNQP20KjYZJEeiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkBK28%2FbtsKEvO5aOw%2FFchrNDEkNQP20KjYZJEeiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1654&quot; height=&quot;1480&quot; data-filename=&quot;스크린샷 2024-11-12 오후 12.30.16.png&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;1480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DisplayLink Manager가 화면을 녹화할 권한이 있으면 완료!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째 시도&lt;/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-filename=&quot;IMG_A3C8A85BB6FB-1.jpeg&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;665&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eoxeoA/btsKEbcsyh8/kQ0RmayrndBrR5xSMKO8k1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eoxeoA/btsKEbcsyh8/kQ0RmayrndBrR5xSMKO8k1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eoxeoA/btsKEbcsyh8/kQ0RmayrndBrR5xSMKO8k1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeoxeoA%2FbtsKEbcsyh8%2FkQ0RmayrndBrR5xSMKO8k1%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;1638&quot; height=&quot;665&quot; data-filename=&quot;IMG_A3C8A85BB6FB-1.jpeg&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;665&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기존에 Dell 모니터에 어댑터를 연결하고, LG 듀얼업은 C 연결선으로 바로 꽂아서 설정을 하니, 제대로 들어온다. 하지만 하나의 문제가 있는데 Dell 모니터의 해상도가 1920 X 1080 세팅을 했음에도 불구하고, 굉장히 흐리흐리 해졌다 ㅠㅠ&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;일단은 하루 이렇게 써보았지만, 이 세팅으로는 함께 할 수 없었다. Dell이 주 모니터인데, 흐리침침 하니까 자꾸 창을 다른 모니터 or 맥북 화면으로 옮기게 되고 그러다 보니 Dell -&amp;gt; 다른 디스플레이로 전환 할 때마다 개안하는 느낌을 받았다. 이대로는 안된다! 떼잉&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 시도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 있을 거야 하며 여러 글들을 찾아보았다. 대부분 여기서 포기하고, 새 맥북을 샀다고 하는 사람들을 다수 포착했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-12 오후 1.00.50.png&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJFXM0/btsKEPfBWa8/Vn5h9O4Nfa1Rbkg1xpJVFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJFXM0/btsKEPfBWa8/Vn5h9O4Nfa1Rbkg1xpJVFK/img.png&quot; data-alt=&quot;나도 그러고 싶다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJFXM0/btsKEPfBWa8/Vn5h9O4Nfa1Rbkg1xpJVFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJFXM0%2FbtsKEPfBWa8%2FVn5h9O4Nfa1Rbkg1xpJVFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;197&quot; data-filename=&quot;스크린샷 2024-11-12 오후 1.00.50.png&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나도 그러고 싶다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 언제나 방법은 있기 마련! 해외 포럼에서 이런 글을 발견해서 나는 이 문제를 해결했다! &lt;a href=&quot;https://www.displaylink.org/forum/showthread.php?p=96983&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.displaylink.org/forum/showthread.php?p=96983&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731383938832&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;2560x2880 with a DL-5500 - DisplayLink Forum&quot; data-og-description=&quot;I have a StarTech USB32DP4K, which has a DL-5500 chip, which works under Ubuntu 24.04. I've been able to drive 4k@30Hz with it. I am now trying to get this to work with an LG DualUP monitor, which has the rather odd resolution of 2560x2880, but can only ge&quot; data-og-host=&quot;www.displaylink.org&quot; data-og-source-url=&quot;https://www.displaylink.org/forum/showthread.php?p=96983&quot; data-og-url=&quot;https://www.displaylink.org/forum/showthread.php?p=96983&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.displaylink.org/forum/showthread.php?p=96983&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.displaylink.org/forum/showthread.php?p=96983&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;2560x2880 with a DL-5500 - DisplayLink Forum&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I have a StarTech USB32DP4K, which has a DL-5500 chip, which works under Ubuntu 24.04. I've been able to drive 4k@30Hz with it. I am now trying to get this to work with an LG DualUP monitor, which has the rather odd resolution of 2560x2880, but can only ge&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.displaylink.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하자면, &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;DL-6950 칩셋을 가진 어댑터는 2560x2880(LG듀얼업 기본 해상도) 해상도 연결이 가능하다는 것이다!&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;당장 물건을 탐색했고, 갓팡에서 바로 다음날 배송되는 물건을 찾았다. 링크는 &lt;a href=&quot;https://www.coupang.com/vp/products/8142465479?vendorItemId=85422303964&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1731384619790&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;맥북 듀얼모니터 어댑터 4K 60Hz 디스플레이링크 DL6950 display link 화면확장 - USB허브 | 쿠팡&quot; data-og-description=&quot;쿠팡에서 4.9 구매하고 더 많은 혜택을 받으세요! 지금 할인중인 다른 68 제품도 바로 쿠팡에서 확인할 수 있습니다.&quot; data-og-host=&quot;www.coupang.com&quot; data-og-source-url=&quot;https://www.coupang.com/vp/products/8142465479?vendorItemId=85422303964&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; data-og-url=&quot;https://www.coupang.com/vp/products/8142465479?vendorItemId=85422303964&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b4HoEJ/hyXwtXRdfd/ffNh7NwbMWjujUvCtkqMmK/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/FQuZL/hyXwsdxjhJ/NKDkImSXG4BluGOK81B6D1/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492&quot;&gt;&lt;a href=&quot;https://www.coupang.com/vp/products/8142465479?vendorItemId=85422303964&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.coupang.com/vp/products/8142465479?vendorItemId=85422303964&amp;amp;sourceType=MyCoupang_my_orders_list_product_title&amp;amp;isAddedCart=&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b4HoEJ/hyXwtXRdfd/ffNh7NwbMWjujUvCtkqMmK/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492,https://scrap.kakaocdn.net/dn/FQuZL/hyXwsdxjhJ/NKDkImSXG4BluGOK81B6D1/img.jpg?width=492&amp;amp;height=492&amp;amp;face=0_0_492_492');&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;맥북 듀얼모니터 어댑터 4K 60Hz 디스플레이링크 DL6950 display link 화면확장 - USB허브 | 쿠팡&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;쿠팡에서 4.9 구매하고 더 많은 혜택을 받으세요! 지금 할인중인 다른 68 제품도 바로 쿠팡에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.coupang.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;판매자에게 직접 문의도 드렸는데 듀얼업 연결 가능하다고 하셔서 바로 구매했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-12 오후 1.10.56.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmM3j/btsKDTXlccL/4USGQVK69wpeOaKUkF6d51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmM3j/btsKDTXlccL/4USGQVK69wpeOaKUkF6d51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmM3j/btsKDTXlccL/4USGQVK69wpeOaKUkF6d51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmM3j%2FbtsKDTXlccL%2F4USGQVK69wpeOaKUkF6d51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;368&quot; data-filename=&quot;스크린샷 2024-11-12 오후 1.10.56.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배송받자마자 바로 설치했다. 기존에 DisplayLink Manager는 이미 설정해 둬서, 연결만 하면 끝!&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_4755.jpg&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;2120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2K8RT/btsKD5KahEr/4pT3jcICStua6xgNbyYlRK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2K8RT/btsKD5KahEr/4pT3jcICStua6xgNbyYlRK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2K8RT/btsKD5KahEr/4pT3jcICStua6xgNbyYlRK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2K8RT%2FbtsKD5KahEr%2F4pT3jcICStua6xgNbyYlRK%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;1590&quot; height=&quot;2120&quot; data-filename=&quot;IMG_4755.jpg&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;2120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 연결했다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 이거지 이거야!&lt;/p&gt;</description>
      <category>IT Review</category>
      <category>dl-6950</category>
      <category>lg듀얼업모니터</category>
      <category>듀얼모니터</category>
      <category>듀얼모니터어댑터</category>
      <category>맥북에어</category>
      <category>맥북에어m1</category>
      <category>맥북에어듀얼모니터</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/158</guid>
      <comments>https://yay-dev.tistory.com/entry/%EC%9E%A5%EB%B9%84%EB%B3%91-%EA%B3%A0%EC%B9%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%98%A8%EB%AA%B8-%EB%B9%84%ED%8B%80%EA%B8%B0-%EB%A7%A5%EB%B6%81-m1-%EC%97%90%EC%96%B4-%EB%93%80%EC%96%BC%EB%AA%A8%EB%8B%88%ED%84%B0-%EC%84%A4%EC%A0%95-with-LG-%EB%93%80%EC%96%BC%EC%97%85#entry158comment</comments>
      <pubDate>Tue, 12 Nov 2024 13:27:07 +0900</pubDate>
    </item>
    <item>
      <title>Kafka vs RabbitMQ &amp;amp; MSA환경에 kafka를 적용한다면? (with 예제코드)</title>
      <link>https://yay-dev.tistory.com/entry/Kafka-vs-RabbitMQ-MSA%ED%99%98%EA%B2%BD%EC%97%90-kafka%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C%EB%8B%A4%EB%A9%B4</link>
      <description>&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;MSA 는 대규모 시스템 설계 아키텍쳐 패턴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RabbitMQ,Kafka&lt;/b&gt; 같은 &lt;code&gt;메시징 시스템&lt;/code&gt;을 활용하여 각 서비스 간의 효율적인 데이터 통신과 확장성을 보장&lt;/li&gt;
&lt;/ul&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-filename=&quot;message-queue-and-rabbitmq-1.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5KSYz/btsKk3rpFTN/2szJL20zk51CZ5HKoflDDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5KSYz/btsKk3rpFTN/2szJL20zk51CZ5HKoflDDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5KSYz/btsKk3rpFTN/2szJL20zk51CZ5HKoflDDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5KSYz%2FbtsKk3rpFTN%2F2szJL20zk51CZ5HKoflDDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1844&quot; height=&quot;808&quot; data-filename=&quot;message-queue-and-rabbitmq-1.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;808&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;Queue 형태로 메세지를 저장. 송신자 (Producer)가 메세지를 큐에 넣으면, 수신자(Consumer)는 자신의 속도에 맞춰 Queue에서 메세지를 처리.&lt;/li&gt;
&lt;li&gt;Message Broker를 사이에 두고, 송신자(Producer)와 수신자(Consumer)가 간접적으로 데이터를 주고 받음&lt;/li&gt;
&lt;li&gt;MSA 환경에 적용한다면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;송신자 (Producer): MSA에서 메세지를 발행하는 곳 (e.g. 주문상태 변경에 대한 메세지를 order-msa에서 발행)&lt;/li&gt;
&lt;li&gt;수신자 (Consumer): MSA에서 메세지를 처리하는 곳 (e.g. 재고차감을 하기 위해 stock-msa에서 주문상태 변경 메세지를 수신)&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;비동기 처리&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer 메세지를 발행한 후, consumer가 메세지를 처리할 때까지 기다리지 않아도 됨 &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;u&gt;&lt;b&gt;데이터 손실 방지&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 메세지 큐에 안전하게 보관. consumer가 메세지를 놓치지 않고 처리 가능 &amp;rarr; 데이터 손실 방지, 신뢰성 있는 통신 보장&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;u&gt;&lt;b&gt;부하 분산&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 consumer가 큐의 메세지를 가져가서 처리 &amp;rarr; 시스템의 부하를 분산. 성능 ⬆️ &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;u&gt;&lt;b&gt;스케일링&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메세지 큐 시스템은 수평적 확장이 용이. 더 많은 Producer, Consumer를 추가 가능 &amp;rarr; 처리 능력 ⬆️&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RabbitMQ vs Kafka&lt;/h3&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/b8t6rn/btsKkdaHziB/iG3RpjwGaFPHXPU1V3UVWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8t6rn/btsKkdaHziB/iG3RpjwGaFPHXPU1V3UVWK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1601&quot; data-origin-height=&quot;715&quot; data-filename=&quot;rabbitmq-implementation.png&quot; width=&quot;692&quot; height=&quot;309&quot; style=&quot;width: 56.0035%; margin-right: 10px;&quot; data-widthpercent=&quot;56.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8t6rn/btsKkdaHziB/iG3RpjwGaFPHXPU1V3UVWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8t6rn%2FbtsKkdaHziB%2FiG3RpjwGaFPHXPU1V3UVWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1601&quot; height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NdtQs/btsKkHPJZcb/cm6IkKsBr0kQN2YzY3ovi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NdtQs/btsKkHPJZcb/cm6IkKsBr0kQN2YzY3ovi0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;1016&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_8.16.48.png&quot; style=&quot;width: 42.8337%;&quot; data-widthpercent=&quot;43.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NdtQs/btsKkHPJZcb/cm6IkKsBr0kQN2YzY3ovi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNdtQs%2FbtsKkHPJZcb%2Fcm6IkKsBr0kQN2YzY3ovi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1740&quot; height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;RabbitMQ 구성도 (좌) Kafka 구성도 (우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 167px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style11&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;&lt;b&gt;차이점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;&lt;b&gt;RabbitMQ&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;&lt;b&gt;Kafka&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 95px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 95px;&quot;&gt;Producer &amp;harr; Consumer의 상호작용 방식&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 95px;&quot;&gt;Producer는 메세지를 보내고 메세지가 의도한 Consumer에 도착했는지 모니터링 &amp;rarr; 우편물을 받아서, 수취인에게 배달하는 우체국&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 95px;&quot;&gt;Producer는 Consumer가 메세지를 검색했는지에 대한 여부와 상관없이 Queue에 저장 &amp;rarr; 다양한 장르의 메세지를 책장에 배치하는 도서관. 도서관 회원 (Consumer)가 책의 내용을 기억&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;아키텍쳐&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;- 복잡한 메시지 라우팅을 위해 설계&lt;br /&gt;- 메세지큐 기반 설계&lt;br /&gt;- push 모델 사용&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;- 더 복잡한 아키텍처를 사용. 처리량이 높은 스트림 이벤트를 관리&lt;br /&gt;- 파디션 기반 설계&amp;nbsp;&lt;br /&gt;- pull 모델 사용&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;메세징 처리 방식 &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;- 메시지 전달의 우선순위를 지정하는 범용 메세지 브로커 &lt;br /&gt;- &lt;span data-token-index=&quot;1&quot;&gt;우선순위 대기열&lt;/span&gt; 지원 &lt;br /&gt;- &lt;span data-token-index=&quot;3&quot;&gt;순서대로&lt;/span&gt; 메세지 처리 &lt;br /&gt;- Consumer가 메세지를 처리 &amp;rarr; 브로커에 &lt;span data-token-index=&quot;5&quot;&gt;확인 (ACK)&lt;/span&gt; 응답 전송 &amp;rarr; 브로커가 대기열에서 메시지&lt;span data-token-index=&quot;7&quot;&gt; 삭제 &lt;/span&gt;- 지연시간을 줄이고, 복잡한 메세지를 분산&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;- 우선순위 대기열 지원 X &lt;br /&gt;- &lt;span data-token-index=&quot;1&quot;&gt;토픽&lt;/span&gt;과 &lt;span data-token-index=&quot;3&quot;&gt;파티션&lt;/span&gt;을 사용하여 메세지 처리. &lt;span data-token-index=&quot;5&quot;&gt;파티션 내부&lt;/span&gt;에서의 순서 보장 (offset)&lt;br /&gt;- 메세지를 &lt;span data-token-index=&quot;7&quot;&gt;로그 파일&lt;/span&gt;에 추가. 보존기간이 만료될 때까지 보관. 로그파일에 있는 메세지는 언제든 데이터 처리 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;성능&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;- 초당 수천 개 메시지 처리 (그 이상은 다중 브로커 설정 필요) &lt;br /&gt;- 지연시간이 짧다&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;- 초당 수백만 개의 메시지 처리 &lt;br /&gt;- 대용량 데이터를 처리한다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 MSA에서 쓰일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;서비스간 &lt;/u&gt;&lt;/b&gt;&lt;code&gt;&lt;b&gt;&lt;u&gt;비동기 통신&lt;/u&gt;&lt;/b&gt;&lt;/code&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;Kafka, RabbitMQ &amp;rarr; 비동기 메시징을 통해 MSA간 통신을 비동기적으로 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;확장성&lt;/u&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;MSA 환경에서는 증가하는 트래픽이나 데이터 양에 대응하여 성능을 유지시키거나 향상 시킬 수 있는 환경이 중요 합니다&lt;/li&gt;
&lt;li&gt;Kafka &amp;rarr; 파티셔닝 (수평적 확장), 클러스터에 브로커 추가 (다운타임 없이 추가 가능)&lt;/li&gt;
&lt;li&gt;RabbitMQ &amp;rarr; 클러스터링, 분산 큐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;데이터 일관성, 신뢰성&lt;/u&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;각 MSA는 독립적으로 데이터를 관리 (e.g. msa 별로 분리된 database 사용) &amp;rarr; 서비스 간 데이터 일관성 유지 중요&lt;/li&gt;
&lt;li&gt;Kafka &amp;rarr; 로그, 메세지 저장 기능으로 데이터 일관성 유지, 메세지를 여러번 읽을 수 있도록 처리 가능&lt;/li&gt;
&lt;li&gt;RabbitMQ &amp;rarr; 메세지 디스크 저장, 다른 큐에 복제 하여 데이터 내구성 유지, 실패한 메세지 재처리 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;서비스 간 결합도 ⬇️&lt;/u&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;Kafka &amp;rarr; 이벤트 기반 아키텍처 지원. 서비스 간 독립적으로 동작 가능.&lt;/li&gt;
&lt;li&gt;RabbitMQ &amp;rarr; 큐 기반 메세징을 통해 서비스 간 메세지 전달.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;✅ &lt;b&gt;요약&lt;/b&gt;&lt;/u&gt;&amp;nbsp; &amp;rarr; RabbitMQ, Kafka는 MSA 에서 각각의 서비스가 독립적으로 동작하면서도 효율적으로 통신할 수록 지원하는 도구!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kafka 를 MSA에 적용한다면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;이벤트 중심 아키텍처 (Event-Driven Architecture)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 MSA간 통신을 이벤트 기반으로 처리. 통신을 담당하는 주체는 Kafka&lt;/li&gt;
&lt;li&gt;예시) &lt;b&gt;주문생성&lt;/b&gt;, &lt;b&gt;결제완료&lt;/b&gt;, &lt;b&gt;배송준비&lt;/b&gt; 등의 이벤트를 Kafka에 전달, 이를 구독하는 다른 서비스가 해당 이벤트를 처리하여 필요한 작업 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;실시간 데이터 처리&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;모니터링과 로깅&lt;/u&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;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 중심 아키텍처 Event-Driven Architecture 란&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Event-driven-Microservice-Architecture.jpg&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vyraU/btsKkFErsje/bPKeaPU6rqxt2pAcAezqmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vyraU/btsKkFErsje/bPKeaPU6rqxt2pAcAezqmk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vyraU/btsKkFErsje/bPKeaPU6rqxt2pAcAezqmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvyraU%2FbtsKkFErsje%2FbPKeaPU6rqxt2pAcAezqmk%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;760&quot; height=&quot;248&quot; data-filename=&quot;Event-driven-Microservice-Architecture.jpg&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;248&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 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;시스템 상태변화나 중요한 작업을 나타내는 객체. (e.g. OrderCreated, DeliveryCompleted)&lt;/p&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;MSA서비스에서 이벤트가 발생했을 때 (주문 생성 요청이 들어왔을 때), 해당 이벤트를 Kafka와 같은 메시징 시스템에 전달&lt;/p&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 이벤트에 관심 있는 MSA들이 해당 이벤트를 구독.&lt;/li&gt;
&lt;li&gt;payment-msa는 order-msa가 발행한 OrderCreated 이벤트를 구독하고, 결제 처리 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;이벤트를 발생한 MSA 서비스와 처리하는 MSA서비스가 비동기적으로 동작&lt;/li&gt;
&lt;li&gt;order-msa는 OrderCreated 이벤트를 발행 후, 다른 MSA들이 처리하는 것을 기다리지 않고 (동기 X), 후에 필요한 비즈니스 로직 수행&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;b&gt;build.gradle&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.kafka:spring-kafka'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'

    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
        ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;application.yml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;spring:
  application:
    name: order-msa
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  kafka:
    bootstrap-servers: localhost:9092
    consumer: # TODO kafka consumer, producer에 대한 설정값 수정은 여기에서
      group-id: ${spring.application.name}-group
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        spring.json.trusted.packages: '*'
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringDeserializer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8083&lt;/code&gt;&lt;/pre&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;KafkaTemplate을 사용해서 이벤트를 발신.&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;e.g. 배송시작 시 &amp;rarr; DeliveryCreatedEvent를 생성해서 Notification-Msa가 관련 알림을 보낼수 있게 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class DeliveryService {
    private final DeliveryRepository deliveryRepository;
        private final KafkaTemplate&amp;lt;String, Object&amp;gt; kafkaTemplate; // 자동 주입

    @Transactional
    public void createDelivery(...) {
                Delivery delivery = new Delivery(...);
                deliveryRepository.save(delivery);

                DeliveryCreatedEvent event = new DeliveryCreatedEvent(...); //이벤트에 필요한 값을 넣어주세요
        kafkaTemplate.send(&quot;delivery-created&quot;, EventSerializer.serialize(event));
    }
}&lt;/code&gt;&lt;/pre&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;@KafkaListener 를 사용하여 메세지를 수신&lt;/li&gt;
&lt;li&gt;groupId는 application.yml에서 설정한 &lt;b&gt;consumer group id&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Transactional
@KafkaListener(topics = &quot;delivery-created&quot;, groupId = &quot;notification-group&quot;)
public void handleDeliveryCreatedEvent(String message) {
    DeliveryCreatedEvent event = EventSerializer.deserialize(message, DeliveryCreatedEvent.class);

    // 이벤트로 해야하는 후 처리 진행 (비즈니스 로직)
    // e.g. 배달완료 이벤트 발행 
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Event-Driven + Kafka 구현 예시&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 이벤트 중심 아키텍처를 맛(?) 볼수 있는 예제 코드 &lt;a href=&quot;https://github.com/yayyz/kafka-msa-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&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;b&gt;&lt;u&gt;기술스택&lt;/u&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;Spring-Kafka&lt;/li&gt;
&lt;li&gt;Kafka, Kafka-UI, Zookeeper, Zipkin&lt;/li&gt;
&lt;li&gt;Spring Cloud-Discovery Client, Server, Gateway&lt;/li&gt;
&lt;li&gt;Spring JPA&lt;/li&gt;
&lt;li&gt;H2&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;이벤트 흐름&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_2.30.16.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRVXMk/btsKj2UrfM8/7I4HqpAwPoDUmJOk1dF6V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRVXMk/btsKj2UrfM8/7I4HqpAwPoDUmJOk1dF6V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRVXMk/btsKj2UrfM8/7I4HqpAwPoDUmJOk1dF6V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRVXMk%2FbtsKj2UrfM8%2F7I4HqpAwPoDUmJOk1dF6V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1276&quot; height=&quot;856&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_2.30.16.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;코드베이스에 포함된 것:&lt;/u&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;docker-compose.yml (zookeeper, kafka, kafka-ui, zipkin)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행이 안된다면,&amp;nbsp;환경변수를 설정해주세요! &lt;code&gt;DOCKER_DEFAULT_PLATFORM=linux/amd64&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주문 생성, 주문 목록 조회 http 테스트 파일&lt;/li&gt;
&lt;li&gt;order-msa, payment-msa, notification-msa skeleton 코드, gateway, eureka&lt;/li&gt;
&lt;li&gt;객체 Serialize, De-Serialize 유틸클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;코드가 제대로 실행되&lt;b&gt;&lt;u&gt;고 있는지 확인 하는 방법&lt;/u&gt;&lt;/b&gt;&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ docker-compose 실행 후 컨테이너 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.48.04.png&quot; data-origin-width=&quot;2928&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P6AjO/btsKkrGdpwQ/iFkhiSQsbjSEca4FFnLPCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P6AjO/btsKkrGdpwQ/iFkhiSQsbjSEca4FFnLPCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P6AjO/btsKkrGdpwQ/iFkhiSQsbjSEca4FFnLPCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP6AjO%2FbtsKkrGdpwQ%2FiFkhiSQsbjSEca4FFnLPCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2928&quot; height=&quot;522&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.48.04.png&quot; data-origin-width=&quot;2928&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;✅&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;모든 msa가 정상적으로 실행되는지 확인 &amp;rarr; Eureka 대시보드 (http://localhost:8761)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.18.22.png&quot; data-origin-width=&quot;2762&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sho2m/btsKlxyQXG2/LQ6ZkePkaYQdmRexy8tju0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sho2m/btsKlxyQXG2/LQ6ZkePkaYQdmRexy8tju0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sho2m/btsKlxyQXG2/LQ6ZkePkaYQdmRexy8tju0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsho2m%2FbtsKlxyQXG2%2FLQ6ZkePkaYQdmRexy8tju0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2762&quot; height=&quot;560&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.18.22.png&quot; data-origin-width=&quot;2762&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;http://localhost:8080&quot;&gt;Kafka UI&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 topic, consumer가 정상 등록되어있는지 확인&lt;/span&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/HSeT0/btsKl10tHCE/1fnTAH7Ww04NUUATYBOP21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HSeT0/btsKl10tHCE/1fnTAH7Ww04NUUATYBOP21/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;948&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_2.05.57.png&quot; style=&quot;width: 59.2751%; margin-right: 10px;&quot; data-widthpercent=&quot;59.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HSeT0/btsKl10tHCE/1fnTAH7Ww04NUUATYBOP21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHSeT0%2FbtsKl10tHCE%2F1fnTAH7Ww04NUUATYBOP21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3840&quot; height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bany3c/btsKl8SOXLA/8dLgdzl3VdKaE0a0N5hCd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bany3c/btsKl8SOXLA/8dLgdzl3VdKaE0a0N5hCd0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2152&quot; data-origin-height=&quot;796&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_2.06.24.png&quot; style=&quot;width: 39.5621%;&quot; data-widthpercent=&quot;40.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bany3c/btsKl8SOXLA/8dLgdzl3VdKaE0a0N5hCd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbany3c%2FbtsKl8SOXLA%2F8dLgdzl3VdKaE0a0N5hCd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2152&quot; height=&quot;796&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;api-test.http 파일을 통해 주문생성 API 실행. 각 Topic에서 메세지가 제대로 들어오는지 확인&lt;/span&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; 결제완료 메세지&lt;/li&gt;
&lt;li&gt;결제 &amp;rarr; 주문생성 메세지&lt;/li&gt;
&lt;li&gt;알림 &amp;rarr; 주문완료&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_8.41.19.png&quot; data-origin-width=&quot;3454&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uAROg/btsKkubNozt/5QcwxXQibjkbzu6pxXvMGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uAROg/btsKkubNozt/5QcwxXQibjkbzu6pxXvMGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uAROg/btsKkubNozt/5QcwxXQibjkbzu6pxXvMGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuAROg%2FbtsKkubNozt%2F5QcwxXQibjkbzu6pxXvMGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3454&quot; height=&quot;902&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_8.41.19.png&quot; data-origin-width=&quot;3454&quot; data-origin-height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;notification-msa에서 주문완료 된 로그 메세지 출력 확인&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&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-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.04.15.png&quot; data-origin-width=&quot;2014&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwnJVg/btsKl43X7d7/LIG1B56iCHGffCgBX3BKl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwnJVg/btsKl43X7d7/LIG1B56iCHGffCgBX3BKl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwnJVg/btsKl43X7d7/LIG1B56iCHGffCgBX3BKl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwnJVg%2FbtsKl43X7d7%2FLIG1B56iCHGffCgBX3BKl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2014&quot; height=&quot;232&quot; data-filename=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2024-09-13_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_9.04.15.png&quot; data-origin-width=&quot;2014&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 알아보고 싶다면  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;이벤트 중심 아키텍처를 적용했을 때 msa에서 transaction 관리는 어떻게 하지?&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Saga Pattern&lt;/li&gt;
&lt;li&gt;Outbox Pattern 등등&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;a href=&quot;https://hudi.blog/saga-pattern/#%EC%BD%94%EB%A0%88%EC%98%A4%EA%B7%B8%EB%9E%98%ED%94%BC-%EA%B8%B0%EB%B0%98-%EC%82%AC%EA%B0%80-Choreography-based-Saga&quot;&gt;https://hudi.blog/saga-pattern/#코레오그래피-기반-사가-Choreography-based-Saga&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Outbox Pattern &amp;rarr; &lt;a href=&quot;https://fullstackdeveloper.guru/2022/05/19/how-to-implement-transactional-outbox-design-pattern-in-spring-boot-microservices/&quot;&gt;https://fullstackdeveloper.guru/2022/05/19/how-to-implement-transactional-outbox-design-pattern-in-spring-boot-microservices/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;그 외, 읽어보면 좋은 글&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/7835/&quot;&gt;https://techblog.woowahan.com/7835/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/ko/compare/the-difference-between-rabbitmq-and-kafka/&quot;&gt;https://aws.amazon.com/ko/compare/the-difference-between-rabbitmq-and-kafka/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/devops</category>
      <category>event-driven</category>
      <category>kafka</category>
      <category>MSA</category>
      <category>msa환경</category>
      <category>RabbitMQ</category>
      <category>메시징시스템</category>
      <category>카프카</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/157</guid>
      <comments>https://yay-dev.tistory.com/entry/Kafka-vs-RabbitMQ-MSA%ED%99%98%EA%B2%BD%EC%97%90-kafka%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%9C%EB%8B%A4%EB%A9%B4#entry157comment</comments>
      <pubDate>Sat, 26 Oct 2024 21:10:58 +0900</pubDate>
    </item>
    <item>
      <title>Leethub v2 수정해서 NOTES.md 도 업로드 하기</title>
      <link>https://yay-dev.tistory.com/entry/Leethub-v2-%EC%88%98%EC%A0%95%ED%95%B4%EC%84%9C-NOTESmd-%EB%8F%84-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;new UI가 적용되면서 기존에 동작하던 Leethub 앱이 동작하지 않게 되었다.&lt;br /&gt;이에 따라서 여러 개발자들이 v2, v3 등등 개선한 버전을 공유하고있다! 대부분의 케이스에서는 잘 동작하는 것 같다.  &lt;br /&gt;이렇게 동작하지 않는 이유로 검색하다가, algorithms 풀이를 한 repo에서 관리할수있게, 그리고 이걸 difficulty 기준으로 폴더 정리해서 나눌수있게! 해주는 &lt;a href=&quot;https://sozerodev.tistory.com/193&quot;&gt;블로그 글&lt;/a&gt;을 발견했다. 이렇게 정리하면 기존에 알고리즘 문제 풀이 하는 repo도 싹 정리가능해서 적용했다! 잘됨!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 쓰다가, 문득 notes도 같이 업로드 되었으면 좋겠다! 는 생각에 기존 코드를 좀 뜯어 보기 시작했다.&lt;br /&gt;나는 지금 V2버전을 수정해서 사용하고있는데, 이 코드 기반으로는 notes가 업로드 되는 부분이 생략되어있다.&lt;br /&gt;Notes도 함께 알고리즘 풀이 repo에서 관리되면 좋겠다&amp;hellip;라는 생각에 몇가지 수정을 하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드를 살펴보면!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V1에서는 (구 leetcode UI에서 정상동작했을 때) &lt;code&gt;getNotesIfAny&lt;/code&gt; 라는 function에서 note의 값을 긁어오는 동작을 하고 있다. 내용은 단순하게 div 로 떠있는 text editor 의 영역을 찾아보고 있으면! 거기 안의 내용을 가져오고 다듬어서 return 하는 그런 function이다.&lt;br /&gt;V1에서는 해당 내용이 구현이 되어있는데 V2에서는 없길래 그냥 간단하게 아래와 같이 내용을 추가했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;LeetCodeV2.prototype.getNotesIfAny = function () { 
  if (!checkElem(document.getElementsByClassName('CodeMirror-code'))) return &quot;&quot;;

  text = document.querySelector('.CodeMirror-code').innerText;
  notes = &quot;&quot;
  if (text) {
    notes = `${text.trim()}`.trim();
  }
  return notes.trim();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면! note가 잘 등록이 된다!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nvji3/btsG4TyH4Xb/4HKuyiIlmtJoLyYYzlvg11/img.png&quot; alt=&quot;https://blog.kakaocdn.net/dn/nvji3/btsG4TyH4Xb/4HKuyiIlmtJoLyYYzlvg11/img.png&quot; /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선점과 버그&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;rarr; Note 등록 충돌&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 잘 쓰다가, Github에서 직접 commit으로 등록한 note는 등록이 안되는 것을 발견했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9NQNQ/btsG3fQj09j/nm5koiOWaKlV3thugtpkmK/img.png&quot; alt=&quot;https://blog.kakaocdn.net/dn/b9NQNQ/btsG3fQj09j/nm5koiOWaKlV3thugtpkmK/img.png&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 데이터 때문에 409 (Conflict Http status code)에러가 발생한다. 그런데 Leetcode에서 등록한 note에 대해서는 이런 에러가 발생하지 않는다..?  &lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;updateNotes = uploadGit(
          btoa(unescape(encodeURIComponent(notes))),
          problemName,
          'NOTES.md',
          createNotesMsg,
          'upload',
          false,
        );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러는 upload하는 이 부분에서 중복값을 저장하려고 할때 conflict error code를 내뱉으며 발생한다.&lt;br /&gt;직접 commit만 안하면 상관없는 경우일것같지만! 개선하면 좋을 것 같아 추가한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;rarr; sessionId가 없는 경우에 github push X&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;submit 할때 URL에 sessionId가 없으면 git push가 이루어 지지 않는다.&lt;br /&gt;최초로 submit은 잘되는데, 수정할때 무시(?) 되는것 같다.&lt;br /&gt;이건 실제로 사용하면서 좀 불편하다고 생각하고있어서, 이것 또한 개선리스트에 추가한다.&lt;br /&gt;앗 그런데 나만 발생하는 에러인가?  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;git repo&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NOTES.md 업로드 하는 부분까지 추가된 코드는 &lt;a href=&quot;https://github.com/yayyz/LeetHub-2.0-Sandbox&quot;&gt;여기&lt;/a&gt;서 확인하실 수 있습니다!&lt;/p&gt;</description>
      <category>devlog/etc</category>
      <category>LeetCode</category>
      <category>leethub</category>
      <category>leethub 오류</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/156</guid>
      <comments>https://yay-dev.tistory.com/entry/Leethub-v2-%EC%88%98%EC%A0%95%ED%95%B4%EC%84%9C-NOTESmd-%EB%8F%84-%EC%97%85%EB%A1%9C%EB%93%9C-%ED%95%98%EA%B8%B0#entry156comment</comments>
      <pubDate>Mon, 3 Jun 2024 21:54:22 +0900</pubDate>
    </item>
    <item>
      <title>모바일, PC 화면 Nginx로 분기 처리하는 방법</title>
      <link>https://yay-dev.tistory.com/entry/mobilewebwebview-NGINX-%EB%B6%84%EA%B8%B0-%EC%B2%98%EB%A6%AC</link>
      <description>&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/bfIjrB/btrRhInJD3D/eQcxdlcNxNI7Gw1sX6Nl70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfIjrB/btrRhInJD3D/eQcxdlcNxNI7Gw1sX6Nl70/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2396&quot; data-origin-height=&quot;1938&quot; data-filename=&quot;스크린샷 2022-11-15 오후 7.14.17.png&quot; style=&quot;width: 67.18040492028547%; margin-right: 10px;&quot; data-widthpercent=&quot;67.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfIjrB/btrRhInJD3D/eQcxdlcNxNI7Gw1sX6Nl70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfIjrB%2FbtrRhInJD3D%2FeQcxdlcNxNI7Gw1sX6Nl70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2396&quot; height=&quot;1938&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuZwMV/btrRiY37OSx/XKQtmcuQnyWYOBHZQ3bdA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuZwMV/btrRiY37OSx/XKQtmcuQnyWYOBHZQ3bdA1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;1998&quot; data-filename=&quot;IMG_0991.PNG&quot; style=&quot;width: 31.6568043820401%;&quot; data-widthpercent=&quot;32.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuZwMV/btrRiY37OSx/XKQtmcuQnyWYOBHZQ3bdA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuZwMV%2FbtrRiY37OSx%2FXKQtmcuQnyWYOBHZQ3bdA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1164&quot; height=&quot;1998&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;PC화면, 모바일 화면 이렇게 다르게 redirect 하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 네이버 화면 처럼, 접속하는 디바이스에 따라 모바일, PC 화면으로 분기 처리를 해야함!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Proxy로 사용되는 Nginx로 해보자!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떻게?&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;error page&lt;/li&gt;
&lt;li&gt;named location&amp;nbsp;&lt;/li&gt;
&lt;li&gt;header에 있는 user-agent 를 기반으로 분기 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;error page 란?&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에러가 발생한 요청을 지정한 uri로 redirect 하는 nginx module&amp;nbsp;&lt;/li&gt;
&lt;li&gt;400대 에러나 500대 에러의 redirect 설정을 하기 위해 보통 사용!&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;하지만! 이미 사용하고있는 에러코드를 덮어쓸수는 없으니, error code 418을 사용해서 PC,mobile 분기 처리를 하도록 하겠다!&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;418 에러코드가 뭐지??&amp;nbsp;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;client error 응답 코드로 서버가 커피 내리기를 거부한다는 의미를 가진다 - 왜냐면 서버는 찻주전자(teapot) 니까!&amp;nbsp;&lt;/li&gt;
&lt;li&gt;딱히 의미없는 에러코드임... &lt;/li&gt;
&lt;li&gt;1998년과 2014년 만우절에 인용됨&amp;nbsp;&lt;/li&gt;
&lt;li&gt;`딱히 의미없는 에러 코드니까 분기 처리 하는 용도로 딱!`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1668602153672&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;error_page 404       /not_found.html; 
error_page 500      /50x.html;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;named location 이란?&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`@location` = named location&amp;nbsp;&lt;/li&gt;
&lt;li&gt;named location은 해당 `location` 에 진입하기 전까지의 $uri 값을 보존함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;error_page, post_action 그리고 try_files로만 사용 가능&lt;/li&gt;
&lt;li&gt;&amp;rarr; 여기서는 error_page 를 지정한 location block으로 요청이 가게끔 해주는 역할을 함!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;user-agent 란?&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-01-04 오후 8.02.14.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmIHKk/btrVmW29NPj/HXqZIKrb5ktSO7f7Jm84kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmIHKk/btrVmW29NPj/HXqZIKrb5ktSO7f7Jm84kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmIHKk/btrVmW29NPj/HXqZIKrb5ktSO7f7Jm84kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmIHKk%2FbtrVmW29NPj%2FHXqZIKrb5ktSO7f7Jm84kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;49&quot; data-filename=&quot;스크린샷 2023-01-04 오후 8.02.14.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;49&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;request header 값으로 서버에게 현재 접속하는 개체의 정보를 표현한다. 주로 application, operating system, vendor 등의 정보를 표시!&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;rarr;&lt;span&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;이 값을 기준으로 nginx 에서 분기 처리를 할 것!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nginx 설정: mobile, pc 분기 처리&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1668602320638&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set $rewrite_rule MOBILEWEB;
if ($http_user_agent ~* 구분값) {
    set $rewrite_rule WEBVIEW;
}
if ($rewrite_rule = MOBILEWEB) {
    return 418;
}&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;rewrite_rule 이라는 nginx 변수를 설정한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default 값은 MOBILEWEB 으로 redirect 되도록 설정 &amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;user_agent에 mobile과 pc를 구분할 수 있는 값을 condition으로 설정한다 &amp;nbsp;`구분값`&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) iPhone, Mac OS 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 mobile, pc 를 위한 분기처리 설정은 되었다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nginx 설정: error page + named location!&lt;/h3&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;error_page 418 = @mobileweb;
recursive_error_pages on;

location @mobileweb {
    access_log 로그경로;
    root target_디렉토리;
    try_files $uri /index.html;
}&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;error_page에서 설정한 named location ( @으로 정의한 값) 로 해당 request를 전달하도록 설정&lt;/li&gt;
&lt;li&gt;위에서 설정한 error code 418이 떨어지는 경우에, 정의한 @mobileweb location block으로 request가 실행되도록 함!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;종합해보면..&lt;/h3&gt;
&lt;pre id=&quot;code_1672831003994&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# eror_page, 에러코드, nginx variable 설정 
error_page 418 = @mobileweb;
recursive_error_pages on;
set $rewrite_rule MOBILEWEB;

# 분기처리 
if ($http_user_agent ~* 구분값) {
    set $rewrite_rule WEBVIEW;
}
if ($rewrite_rule = MOBILEWEB) {
    return 418;
}

# 요청을 원하는 곳으로! 
location @mobileweb {
    access_log 로그경로;
    root target_디렉토리;
    try_files $uri /index.html;
}&lt;/code&gt;&lt;/pre&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;418 error code:&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;nginx erorr page: &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page&quot;&gt;http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;nginx named location: &lt;a href=&quot;https://serverfault.com/questions/509327/can-we-jump-to-another-location-from-a-location-in-nginx&quot;&gt;https://serverfault.com/questions/509327/can-we-jump-to-another-location-from-a-location-in-nginx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/nginx</category>
      <category>418 status code</category>
      <category>NGINX</category>
      <category>nginx error_page</category>
      <category>nginx if</category>
      <category>nginx named location</category>
      <category>nginx 분기처리</category>
      <category>pc mobile 분기처리</category>
      <category>엔진엑스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/132</guid>
      <comments>https://yay-dev.tistory.com/entry/mobilewebwebview-NGINX-%EB%B6%84%EA%B8%B0-%EC%B2%98%EB%A6%AC#entry132comment</comments>
      <pubDate>Wed, 4 Jan 2023 20:19:33 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 347. Top K Frequent Elements</title>
      <link>https://yay-dev.tistory.com/entry/ArraysHashing-347-Top-K-Frequent-Elements</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/top-k-frequent-elements/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/top-k-frequent-elements/description/&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intuition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 array에서 가장 중복이 많은 k개의 element를 결과값으로 반환&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그렇다면 정렬하고, 중복을 찾기 위해선 hash를 써야겠군&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Approach&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hash를 쓰기 위해서는 python의 dictionary를 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;가장 많이 중복되는 element 별로 저장된 dictionary를 그 후에 정렬!&lt;/li&gt;
&lt;li&gt;정렬된 중복_숫자_맵 에서 k개의 key값을 반환&amp;nbsp;&lt;/li&gt;
&lt;li&gt;dict에서 get()함수는 parameter가 2개! 두번째 param은 optional로 조회하는 key 값이 없는 경우에 반환할 값을 설정&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;numMap[num] = numMap.get(num,0) &amp;rarr; numMap에서 num값의 키값이 없는 경우 0을 리턴&amp;nbsp;&lt;/li&gt;
&lt;li&gt;dict[key] 로 조회했을 때 key가 없다면 에러 발생&amp;nbsp;&lt;/li&gt;
&lt;li&gt;dict.get(key)로 조회했을 때 key가 없다면 None 리턴&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 complexity는 좀 고민을 해보자..&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;first submission:&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672829067235&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def topKFrequent(self, nums: List[int], k: int) -&amp;gt; List[int]:

        numMap = dict()
        for num in nums: 
            numMap[num] = nums.count(num)
        
        ret = dict(sorted(numMap.items(), key=lambda x: x[1], reverse=True))
        return list(ret)[0:k]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;time limit exeeded :( 두둥&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-01-04 오후 7.29.46.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfG3XW/btrVjMAoMzG/vNB76k9ekcOAno0a2vPvJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfG3XW/btrVjMAoMzG/vNB76k9ekcOAno0a2vPvJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfG3XW/btrVjMAoMzG/vNB76k9ekcOAno0a2vPvJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfG3XW%2FbtrVjMAoMzG%2FvNB76k9ekcOAno0a2vPvJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;144&quot; data-filename=&quot;스크린샷 2023-01-04 오후 7.29.46.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 count() 함수는 아니었나봐,, 더 쉬운 방법으로!&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;2nd submission:&lt;/p&gt;
&lt;pre id=&quot;code_1672808324854&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def topKFrequent(self, nums: List[int], k: int) -&amp;gt; List[int]:

        numMap = dict()
        for num in nums: 
            numMap[num] = numMap.get(num,0) + 1
        
        ret = dict(sorted(numMap.items(), key=lambda x: x[1], reverse=True))
        return list(ret)[0:k]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>array</category>
      <category>hash</category>
      <category>LeetCode</category>
      <category>leetcode 347</category>
      <category>Python</category>
      <category>Top K Frequent Elements</category>
      <category>릿코드</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/149</guid>
      <comments>https://yay-dev.tistory.com/entry/ArraysHashing-347-Top-K-Frequent-Elements#entry149comment</comments>
      <pubDate>Wed, 4 Jan 2023 13:59:01 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 49. Group Anagrams</title>
      <link>https://yay-dev.tistory.com/entry/ArraysHashing-Group-Anagrams</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/group-anagrams/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/group-anagrams/description/&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intuition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 길이를 가지고 동일한 문자열 구성을 가져야 하는 anagram!&amp;nbsp;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그럼 저번에 문제 풀이 했을 때 처럼 set을 사용해서 풀어볼까?&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그런데 이 전 문제와 다르게 grouping 해야하는 문제니까 hash 를 써야할듯&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Approach&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단어가 3글자라고 주어진 input에서 3개 묶음의 단어가 다 있으리라고는 보장 X&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;있는 element를 기준으로 hash에 저장해서 확인해야함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;python에서 hash는? &amp;rarr; &lt;a href=&quot;https://docs.python.org/3/c-api/dict.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dictionary&lt;/a&gt;!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dict의 key 값은 정렬한 값으로 저장 &amp;rarr; 모든 element가 정렬된 값을 기준으로 hasKey() operation을 해야하므로&amp;nbsp;&lt;/li&gt;
&lt;li&gt;values() 는 strs의 element 리스트&amp;nbsp;&lt;/li&gt;
&lt;li&gt;각 strs의 element를 확인하고, sorted 된 결과값의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key가 존재한다면 &amp;rarr; 해당 values 에 추가&amp;nbsp;&lt;/li&gt;
&lt;li&gt;없다면 &amp;rarr; 새로운 group 추가&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Time complexity:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(NlogN)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Space complexity:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;pre id=&quot;code_1672719839588&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def groupAnagrams(self, strs: List[str]) -&amp;gt; List[List[str]]:
        # create a dictionary for anagrams
        # sort word and check if that word exist in a set (anagram dictionary)
        # if yes, add, if not pass 

        anagrams = dict()
        for str in strs: 
            word = &quot;&quot;.join(sorted(str))
            # if empty 
            if not anagrams: 
                anagrams[word] = [str]
            else: 
                if word in anagrams: 
                    anagrams[word].append(str)
                else: 
                    anagrams[word] = [str]
        
        return anagrams.values()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 초보의 사소한 실수: sorted(string) 하면 그대로 정렬된 string 값을 내어주는줄 알았다..array가 return 값일줄은..!&lt;/p&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>BE/algorithm</category>
      <category>algorithm</category>
      <category>Arrays</category>
      <category>Group Anagrams</category>
      <category>hash</category>
      <category>LeetCode</category>
      <category>leetcode 75</category>
      <category>릿코드</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/148</guid>
      <comments>https://yay-dev.tistory.com/entry/ArraysHashing-Group-Anagrams#entry148comment</comments>
      <pubDate>Tue, 3 Jan 2023 13:44:40 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 1. Two Sum</title>
      <link>https://yay-dev.tistory.com/entry/ArraysHashing-Two-Sum</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/two-sum/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/two-sum/description/&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intuition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 array의 element 중에 target 값으로 합의 수가 맞는 조합을 찾아야 하면..모든 element를 합해서 확인해 보자&lt;/li&gt;
&lt;li&gt;first + second index element의 합을 확인하고 넘어가야 한다&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Approach&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;당장 생각나는건...outer loop에서는 index 0 부터 좌항에 들어가는 인자를 iterate하고 inner loop에서는 우항에 들어가는 인자를 iterate 해서 둘의 합을 확인하는 방법&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Time complexity:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(N^2)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Space complexity:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;pre id=&quot;code_1672665869081&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def twoSum(self, nums: List[int], target: int) -&amp;gt; List[int]:
        
        done = False  
        for ptr1, i in enumerate(nums):
            ptr2 = ptr1 + 1
            while ptr2 &amp;lt; len(nums):
                if (nums[ptr1] + nums[ptr2] == target):
                    done = True 
                    break
                else: 
                    ptr2 += 1
            if (done):
                break

        return [ptr1, ptr2]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 O(N^2) 풀이 보다 더 좋은 방법 없을까?&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>array</category>
      <category>hash</category>
      <category>LeetCode</category>
      <category>Python</category>
      <category>time complexity</category>
      <category>two sum</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/146</guid>
      <comments>https://yay-dev.tistory.com/entry/ArraysHashing-Two-Sum#entry146comment</comments>
      <pubDate>Tue, 3 Jan 2023 07:35:41 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 242. Valid Anagram</title>
      <link>https://yay-dev.tistory.com/entry/ArraysHashing-Valid-Anagram</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intuition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Anagram은 주어진 문자열의 character를 재배치 했을 때에도 다 같은 구성의 character를 가지는 단어 혹은 문구를 뜻한다고 한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그러니 길이가 같아야지?&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;같은 구성의 character를 가져야하겠네?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Approach&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 구성의 문자를 가진것을 어떻게 비교하면 좋지?&amp;nbsp;
&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;길이가 다른 경우는 먼저 False를 반환 하는게 좋을 것 같다&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Time complexity:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(nlogn) 평균 / W.C (= worst case)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이유는 python의 sorted() 함수는 Timesort라는 정렬 알고리즘을 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Timesort 알고리즘이란?&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;insertion sort 와 merge sort의 장점만을 합쳐서 만든 hybrid sorting algorithm&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;평균 &amp;amp; W.C = O(NlogN) 을 보장&amp;nbsp;&lt;/li&gt;
&lt;li&gt;참고: &lt;a href=&quot;https://realpython.com/sorting-algorithms-python/#the-timsort-algorithm-in-python&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://realpython.com/sorting-algorithms-python/#the-timsort-algorithm-in-python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Space complexity:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(1) 평균 / O(n) W.C&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;pre id=&quot;code_1671004934474&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def isAnagram(self, s: str, t: str) -&amp;gt; bool:
        if len(s) != len(t): return False
        return sorted(s) == sorted(t)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>array</category>
      <category>hash</category>
      <category>LeetCode</category>
      <category>Python</category>
      <category>space complexity</category>
      <category>time complexity</category>
      <category>valid anagrams</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/144</guid>
      <comments>https://yay-dev.tistory.com/entry/ArraysHashing-Valid-Anagram#entry144comment</comments>
      <pubDate>Wed, 14 Dec 2022 17:11:54 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 217. Contains Duplicate</title>
      <link>https://yay-dev.tistory.com/entry/Contains-Duplicate</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intuition&lt;/h3&gt;
&lt;!-- Describe your first thoughts on how to solve this problem. --&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;python 으로 문제 풀이를 하기로 했으니 counter 함수를 사용해서 풀이하면 될것같다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Approach&lt;/h3&gt;
&lt;!-- Describe your approach to solving the problem. --&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;counter 함수로 첫번째 풀이를 했는데, time limit exceeded 가 떴음&lt;/li&gt;
&lt;li&gt;두번째 제출에서는 set 자료구조를 사용해서 풀이
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;set은 중복을 허용 X&lt;/li&gt;
&lt;li&gt;주어진 data set을 set에 넣으면 중복이 사라져서 전체 사이즈가 달라짐&lt;/li&gt;
&lt;li&gt;set의 사이즈와 nums의 사이즈를 비교!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Time complexity:&lt;!-- Add your time complexity here, e.g. $$O(n)$$ --&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;first attempt:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(N^2)&lt;/li&gt;
&lt;li&gt;list.count() 함수는 내부적으로 모든 PyObject를 iterate 하고, 주어진 입력값과 compare 하기 때문에 O(N) complexity를 가진다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고: &lt;a href=&quot;https://stackoverflow.com/questions/44812468/what-is-the-time-complexity-of-python-lists-count-function&quot;&gt;https://stackoverflow.com/questions/44812468/what-is-the-time-complexity-of-python-lists-count-function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;second attempt:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Space complexity:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;first attempt:&lt;/li&gt;
&lt;li&gt;second attempt:&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;first attempt&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class Solution:
    def containsDuplicate(self, nums: List[int]) -&amp;gt; bool:
        for num in nums: 
            if (nums.count(num) &amp;gt;= 2): 
                return True 
            else: 
                continue

        return False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;second&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Solution:
    def containsDuplicate(self, nums: List[int]) -&amp;gt; bool:

        return len(nums) != len(set(nums))
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>array</category>
      <category>contains duplicate</category>
      <category>hash</category>
      <category>LeetCode</category>
      <category>space complexity</category>
      <category>time complexity</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/143</guid>
      <comments>https://yay-dev.tistory.com/entry/Contains-Duplicate#entry143comment</comments>
      <pubDate>Wed, 14 Dec 2022 13:31:23 +0900</pubDate>
    </item>
    <item>
      <title>custom header 값을 nginx 로그로 찍기</title>
      <link>https://yay-dev.tistory.com/entry/custom-http-header-%EA%B0%92%EC%9D%84-nginx-%EB%A1%9C%EA%B7%B8%EB%A1%9C-%EC%B0%8D%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;기본&amp;nbsp;http&amp;nbsp;header들은&amp;nbsp;nginx&amp;nbsp;에서&amp;nbsp;제공하는&amp;nbsp;variable로&amp;nbsp;찍으면 되는데..어플리케이션에서&amp;nbsp;직접&amp;nbsp;생성한 `custom&amp;nbsp;header` 는&amp;nbsp;어떻게&amp;nbsp;찍을까?&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-27 오후 4.45.34.png&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccV9wA/btrPOSRDdew/O8o3wPaRdisWkCbcFKtWEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccV9wA/btrPOSRDdew/O8o3wPaRdisWkCbcFKtWEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccV9wA/btrPOSRDdew/O8o3wPaRdisWkCbcFKtWEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccV9wA%2FbtrPOSRDdew%2FO8o3wPaRdisWkCbcFKtWEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;865&quot; height=&quot;82&quot; data-filename=&quot;스크린샷 2022-10-27 오후 4.45.34.png&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우선 nginx 에서 지원하는 variable 리스트 중에 `$http_name` 이라는 값이 있다. 저 뒤 `_name` 부분에 custom 하게 생성한 header의 이름을 넣어주면 log에 기록할 수 있게 된다!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;log_format에 아래와 같이 추가해주면! 예쁘게 잘 찍힐것!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666936138343&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;log_format  main '$remote_addr $http_x_forwarded_for [$time_local] '
                     '$host $request $status $body_bytes_sent '
                     '$request_time $upstream_response_time '
                     '&quot;$http_referer&quot; &quot;$http_user_agent&quot; &quot;$http_커스텀헤더명&quot;';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/nginx</category>
      <category>custom header log</category>
      <category>http header</category>
      <category>NGINX</category>
      <category>nginx header</category>
      <category>nginx log</category>
      <category>nginx log format</category>
      <category>nginx logformat</category>
      <category>엔진엑스</category>
      <category>커스텀 헤더</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/139</guid>
      <comments>https://yay-dev.tistory.com/entry/custom-http-header-%EA%B0%92%EC%9D%84-nginx-%EB%A1%9C%EA%B7%B8%EB%A1%9C-%EC%B0%8D%EA%B8%B0#entry139comment</comments>
      <pubDate>Thu, 27 Oct 2022 19:06:18 +0900</pubDate>
    </item>
    <item>
      <title>[Redis] Sorted Sets</title>
      <link>https://yay-dev.tistory.com/entry/Redis-Sorted-Sets</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keys 명령어
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;returns all keys matching pattern.&lt;/li&gt;
&lt;li&gt;O(N)&lt;/li&gt;
&lt;li&gt;데이터가 많은 DB를 상대로 수행하는 경우에 성능저하 발생&lt;/li&gt;
&lt;li&gt;리얼 환경에서 사용하는 것을 지양&lt;/li&gt;
&lt;li&gt;해당 명령어는 디버깅, 키스페이스 레이아웃을 변경하는 것 같이 특별한 상황에만 사용하는 것을 권장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sorted Sets
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(log(n))&lt;/li&gt;
&lt;li&gt;중복이 없는 문자열 콜렉션.&lt;/li&gt;
&lt;li&gt;모든 데이터는 score와 매핑되어서 정렬되고 데이터자체는 유니크한 값이지만, score는 중복 될 수 있다.&lt;/li&gt;
&lt;li&gt;Sorted Sets에 데이터가 등록될 떄 순서대로 등록되기 때문에 (등록된 이후에 후처리로 정렬하는 것이 아님), 데이터의 범위와 랭크를 아주 빠르게 구할 수 있다.&lt;/li&gt;
&lt;li&gt;중간에 있는 데이터에 접근 하는 것도 굉장히 빠르게 처리 가능.&lt;/li&gt;
&lt;li&gt;배민에서 사용한 대표적인 command
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ZADD: (데이터를 순서대로 저장)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(log(n))&lt;/li&gt;
&lt;li&gt;이미 있는 데이터를 다시 입력하는 경우 score가 업데이트 됨.&lt;/li&gt;
&lt;li&gt;같은 score를 가진 데이터가 존재하는 경우에는 데이터의 바이너리 표현으로 정렬한다 (The lexicographic ordering used is binary, it compares strings as array of bytes)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ZRANK: (대기순번 제공)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(logN)&lt;/li&gt;
&lt;li&gt;조회하고자 하는 데이터의 랭크를 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ZRANGE: (일정한 수만큼 대기열에서 참가열로 이동)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(logN + M) / N = num of elements, M = numb of elements returned&lt;/li&gt;
&lt;li&gt;조회하고자 하는 데이터의 범위를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배민에서는 대기열과 참가열로 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sorted Sets의 내부구조 (Skip List)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이곳을 참고하여 테스트 해보기 &lt;a href=&quot;http://redisgate.kr/redis/configuration/internal_skiplist.php&quot;&gt;http://redisgate.kr/redis/configuration/internal_skiplist.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/cache</category>
      <category>redis</category>
      <category>redis sorted sets</category>
      <category>sorted sets</category>
      <category>예전기록</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/134</guid>
      <comments>https://yay-dev.tistory.com/entry/Redis-Sorted-Sets#entry134comment</comments>
      <pubDate>Fri, 7 Oct 2022 12:30:09 +0900</pubDate>
    </item>
    <item>
      <title>[docker] docker-compose 업데이트! 무엇이 바뀌었고 언제까지 바꿔야 하는가?</title>
      <link>https://yay-dev.tistory.com/entry/docker-docker-compose-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%B0%94%EB%80%8C%EC%97%88%EA%B3%A0-%EC%96%B8%EC%A0%9C%EA%B9%8C%EC%A7%80-%EB%B0%94%EA%BF%94%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;무엇이 바뀌었는가&lt;/u&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내부 코드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v1 은 python으로 개발됨&lt;/li&gt;
&lt;li&gt;v2 는 Golang으로 개발! (from scratch!)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;standalone binary 파일이 아니라서 설치 방식이 v1과 좀 다름docker-compose &amp;rarr; docker compose&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;명령어 사이에 &lt;code&gt;-&lt;/code&gt; 없이 사용 가능&lt;/li&gt;
&lt;li&gt;v1과 v2의 compatibility를 위해 &lt;code&gt;compose-switch&lt;/code&gt; 유틸 제공
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker-compose python 코드를 docker compose Golang으로 converting!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;container 명명 방식 변경&lt;/h4&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;container 이름 기준으로 스크립트를 개발하였다면, &lt;code&gt;-compatibility&lt;/code&gt;옵션을 사용하여 에러가 발생하지 않도록 설정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BuildKit supported by default&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v1에서는 default가 아니었음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BuildKit&lt;/b&gt;?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile을 docker image로 변경해주는 open-source project&lt;/li&gt;
&lt;li&gt;docker image만 생성하는게 아니라 OCI image 나 다른 format의 output으로도 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마스코트 이름 (공모중)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nmEGg/btrNZkWAWMY/9JxYJ25KPpoZNZPmV8u4Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nmEGg/btrNZkWAWMY/9JxYJ25KPpoZNZPmV8u4Hk/img.png&quot; data-alt=&quot;귀여울..지도?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nmEGg/btrNZkWAWMY/9JxYJ25KPpoZNZPmV8u4Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnmEGg%2FbtrNZkWAWMY%2F9JxYJ25KPpoZNZPmV8u4Hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;714&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;귀여울..지도?&lt;/figcaption&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;도커 모비딕 외에 docker compose도 마스코트가 있다는 사실...아셨나요?&lt;/li&gt;
&lt;li&gt;현재 이 마스코트의 이름을 공모중이라고 합니다. 관심있으신 분은 &lt;a href=&quot;https://github.com/docker/compose/issues/9417&quot;&gt;여기서&lt;/a&gt; 참여해 보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;v1 &amp;rarr; v2 업그레이드 이점&lt;/u&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker CLI의 빠른! 새로운 기능 제공받을 수 있음&lt;/h4&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;GPU machine support&lt;/li&gt;
&lt;li&gt;profiles 기능 추가 (자세한 내용: &lt;a href=&quot;https://docs.docker.com/compose/profiles/&quot;&gt;https://docs.docker.com/compose/profiles/&lt;/a&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;code&gt;profiles&lt;/code&gt;설정이 없는 서비스는 항상 활성화 됨&lt;/li&gt;
&lt;li&gt;사용방법 ex) &lt;code&gt;docker compose --profile debug up&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;version: &quot;3.9&quot;
services:
  frontend:
    image: frontend
    profiles: [&quot;frontend&quot;]

  phpmyadmin:
    image: phpmyadmin
    depends_on:
      - db
    profiles:
      - debug

  backend:
    image: backend

  db:
    image: mysql&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;code&gt;docker compose ls&lt;/code&gt; 명령어가 이제 모든 compose app을 보여줌&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker compose cp&lt;/code&gt; 명령어는 서비스 컨테이너 or 로컬 파일시스템 간에 파일이나 폴더를 복제할수있게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스무-스 한 배포 방식&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라우드 서비스와의 연동으로 &lt;code&gt;compose up&lt;/code&gt;만 하면 배포/실행 가능&lt;/li&gt;
&lt;li&gt;ex) 개발환경은 로컬, 운영은 ECS에서 한다면, Docker context switch만 하면 원하는 target 환경에 배포 가능&lt;/li&gt;
&lt;li&gt;더 자세한 내용: &lt;a href=&quot;https://github.com/docker/compose-cli&quot;&gt;https://github.com/docker/compose-cli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker 생태계에서의 아싸 탈출&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v1은 python으로 개발되어서 이전에는 docker 생태계에서 아싸였음&lt;/li&gt;
&lt;li&gt;Moby, the CLI 나 다른 Go 기반 프로젝트들의 코드를 사용할 수 있게 됨. (v1에서는 python 코드로의 변환이 필요했었음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Compose binaries로 쉬운 업데이트, 의존성 관리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빨라집니다&lt;/li&gt;
&lt;li&gt;제가 이 부분 내용 이해를 못했습니다. 번역해주실 분 구합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HName/btrNYeQgz55/zynKszrJlzHDYGRnoZWjbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HName/btrNYeQgz55/zynKszrJlzHDYGRnoZWjbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HName/btrNYeQgz55/zynKszrJlzHDYGRnoZWjbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHName%2FbtrNYeQgz55%2FzynKszrJlzHDYGRnoZWjbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;420&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;compose 파일 없이 명령어 실행&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;container label로 프로젝트 실행 가능 (아래 예시)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;--project-name&lt;/code&gt;은 &lt;code&gt;-p&lt;/code&gt; 로 대체가능
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;docker commpose --project-name [프로젝트-라벨] down&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위와같이 docker-compose.yml 파일 이나 환경변수 없이 &lt;code&gt;docker compose start/stop/pause/down/ps/exec&amp;hellip;&lt;/code&gt; 명령어 실행 가능&lt;/li&gt;
&lt;li&gt;container label이 뭔가요? (아래 예시)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너 metadata, 여러 이미지/container 관리에 용이
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;build:
context: .
labels:
com.example.description: &quot;Accounting webapp&quot;
com.example.department: &quot;Finance&quot;
com.example.label-with-empty-value: &quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;v1은 그럼 어떻게 되나요?&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충분히 시간을 줄테니 v2로 옮기세요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deprecated 로 설정&lt;/li&gt;
&lt;li&gt;치명적인 vulnerability or 버그만 다음 마일스톤까지 패치&lt;/li&gt;
&lt;li&gt;v2 필수적으로 사용하지 않을 수 있도록 옵션 제공
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker-compose disable-v2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EOL (=End-of-Life) 제안 기간&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGEy0/btrNYVvZCv7/RNvNI4SFUlLm65TYDr3sB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGEy0/btrNYVvZCv7/RNvNI4SFUlLm65TYDr3sB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGEy0/btrNYVvZCv7/RNvNI4SFUlLm65TYDr3sB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGEy0%2FbtrNYVvZCv7%2FRNvNI4SFUlLm65TYDr3sB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;764&quot; height=&quot;501&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;501&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;2022년 10월&lt;/li&gt;
&lt;li&gt;운명인가요? 어떻게 딱 10월에 v2가 나온걸 알게된거지....&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;더 알고싶다면..&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker compose v2 문서: &lt;a href=&quot;https://www.docker.com/blog/announcing-compose-v2-general-availability/&quot;&gt;https://www.docker.com/blog/announcing-compose-v2-general-availability/&lt;/a&gt;&lt;/p&gt;</description>
      <category>DevOps/docker</category>
      <category>docker</category>
      <category>docker compose</category>
      <category>docker compose v1 v2 변경사항</category>
      <category>docker compose v1 v2 차이점</category>
      <category>docker compose v2</category>
      <category>docker compose 업데이트</category>
      <category>docker-compose</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/135</guid>
      <comments>https://yay-dev.tistory.com/entry/docker-docker-compose-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%B0%94%EB%80%8C%EC%97%88%EA%B3%A0-%EC%96%B8%EC%A0%9C%EA%B9%8C%EC%A7%80-%EB%B0%94%EA%BF%94%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80#entry135comment</comments>
      <pubDate>Thu, 6 Oct 2022 17:27:31 +0900</pubDate>
    </item>
    <item>
      <title>[뉴비개발자 시리즈] 신입 개발자의 일탈 - 봇 만들기</title>
      <link>https://yay-dev.tistory.com/entry/%EC%8B%A0%EC%9E%85-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%9D%BC%ED%83%88-%EB%B4%87-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2017-09-29에 작성한 글을 옮겨왔습니다. 동기들 채팅방에서 썼던 봇에 관한 글입니다. 방정맞은 말투는 이해 부탁드립니다..&lt;br /&gt;지금 보니 신입이었을땐 딴짓도 개발로 했었네요  &lt;/blockquote&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;614&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdI6eO/btrNRCxdwDO/gFB51ouXg24kfbum1FhLeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdI6eO/btrNRCxdwDO/gFB51ouXg24kfbum1FhLeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdI6eO/btrNRCxdwDO/gFB51ouXg24kfbum1FhLeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdI6eO%2FbtrNRCxdwDO%2FgFB51ouXg24kfbum1FhLeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;614&quot; height=&quot;242&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;242&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;br /&gt;네&lt;br /&gt;1분안에 완성 가능합니다.&lt;br /&gt;예.&lt;br /&gt;단축안해서 업무가 손에 안잡혀서 글 올려봅니다.&lt;br /&gt;코드랄 것도 없지만 파이썬으로 &lt;span style=&quot;color: #333333;&quot;&gt;★우리회사&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;★ 로 메세지를 날려보겠습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;import requests, time, datetime, sys

def let_the_world_know():
    hook_url=[사내 메신저 채팅방 web hook url] 
    image_url=[이미지 업로드된 cdn url]
    lunch_url=[세상에서 제일 중요한 그날의 점심 메뉴 캡쳐 이미지 url]
    text = sys.argv[1]
    data = {
        'botName' : '도롱뇽농떙이봇',
        'botIconImage' : image_url,
        'text' : &quot;&quot;,
        'attachments': [{
            &quot;title&quot;: &quot;하&quot;,
            &quot;titleLink&quot; : [점심메뉴 페이지로 가는 direct url],
            &quot;text&quot; : text,
            &quot;color&quot; :&quot;lightpink&quot;
        }]
        }
    response=requests.post(hook_url, json=data)
    
let_the_world_know()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 이게 답니다. 인자로 원하는 넣기만 하면 됩니다.&lt;br /&gt;실행은 요렇게 합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 i_want_to_go_home.py (원하는 메세지)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼..이만..20000&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;앞으로의 개선사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 (9/29) 출범한 베베 프로젝트이니 앞으로 더 많은 기능 추가 가능합니다.&lt;br /&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;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 검색엔진 추가&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 채팅방에서 &lt;code&gt;도롱뇽농땡이봇&lt;/code&gt;호출&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;del&gt;맛집 크롤링 (일단 개발자A 사우님 집들이부터..)&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>devlog/etc</category>
      <category>2017</category>
      <category>Python</category>
      <category>webhook</category>
      <category>봇 만들기</category>
      <category>신입 개발자</category>
      <category>신입 개발자 썰</category>
      <category>채팅 봇</category>
      <category>추억여행</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/130</guid>
      <comments>https://yay-dev.tistory.com/entry/%EC%8B%A0%EC%9E%85-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%9D%BC%ED%83%88-%EB%B4%87-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry130comment</comments>
      <pubDate>Thu, 6 Oct 2022 12:30:18 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] Annotation Targets</title>
      <link>https://yay-dev.tistory.com/entry/Kotlin-Annotation-Targets-1</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2019-06-12 에 작성한 글을 옮겨왔습니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;kotlin-annotation-targets.png&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;23&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaHnRN/btrNOI5Psij/OaTjpmpC5OG0OUW5KYxAk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaHnRN/btrNOI5Psij/OaTjpmpC5OG0OUW5KYxAk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaHnRN/btrNOI5Psij/OaTjpmpC5OG0OUW5KYxAk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaHnRN%2FbtrNOI5Psij%2FOaTjpmpC5OG0OUW5KYxAk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;908&quot; height=&quot;23&quot; data-filename=&quot;kotlin-annotation-targets.png&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;23&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Doc 어노테이션을 붙였을 때 문서화가 안되는 에러가 발생하였습니다.&lt;br /&gt;디컴파일된 자바코드를 확인해보기 전까지는 다른 건 Java로 짰는데 이 클래스는 코틀린이라서 그런가? 라는 말도 안되는 의심을 했습니다..!:joy:&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코틀린은...&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린의 property는 자바의 필드, getter, setter, 그리고 접근자의 파라미터로 해석될 수 있습니다.&lt;br /&gt;그리고 코틀린의 property가 primary constructor에 선언 된 경우에는 생성자의 파라미터로도 해석이 됩니다.&lt;br /&gt;의도하지 않은 방향으로 코드가 생성 될 수도 있으므로, annotation의 use-site 같이 명확한 용도를 정의해줘야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Annotation Use-site Targets&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;annotation의 용도를 선언하기 위해 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;use-site 목록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;property (Java annotation은 해당 값 사용할 수 없음)&lt;/li&gt;
&lt;li&gt;field&lt;/li&gt;
&lt;li&gt;get (getter)&lt;/li&gt;
&lt;li&gt;set (setter)&lt;/li&gt;
&lt;li&gt;receiver&lt;/li&gt;
&lt;li&gt;param (생성자 파라미터)&lt;/li&gt;
&lt;li&gt;setparam&lt;/li&gt;
&lt;li&gt;delegate&lt;/li&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;/ul&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;1099&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M1zrk/btrNNn2n1oS/ml8RucKOJGlWbcJKhhYZ11/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M1zrk/btrNNn2n1oS/ml8RucKOJGlWbcJKhhYZ11/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M1zrk/btrNNn2n1oS/ml8RucKOJGlWbcJKhhYZ11/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM1zrk%2FbtrNNn2n1oS%2Fml8RucKOJGlWbcJKhhYZ11%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;1099&quot; height=&quot;477&quot; data-origin-width=&quot;1099&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;use-site target&lt;/code&gt; 을 명시하지 않으면 사용한 annotation의 @Target 이 사용됩니다.&lt;br /&gt;ex)&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 다수의 @Target이 존재한다면, 적용 가능한 use-site부터 순서대로 적용됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;param&lt;/li&gt;
&lt;li&gt;property&lt;/li&gt;
&lt;li&gt;field&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;DocGenerator.java 는 &lt;code&gt;필드&lt;/code&gt;를 기준으로 하여 @Doc 어노테이션이 붙은 필드를 가져와서 문서화 합니다.&lt;/p&gt;
&lt;pre class=&quot;monkey&quot;&gt;&lt;code&gt;...
for (Field field : fieldList) {
            Doc doc = field.getAnnotation(Doc.class);
            if (doc != null) {
            ...&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수정 전&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 났을 경우의 data class는 일반 자바 클래스에서 어노테이션을 사용하는 것과 같이 작성하였습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;data class ReceiverResponse(@Doc(desc = &quot;수령자 수&quot;, example = &quot;&quot;)
                            val receiverCount: Long,
                            @Doc(desc = &quot;수령자 정보&quot;, example = &quot;&quot;, child = true)
                            val receivers: List&amp;lt;Receiver&amp;gt;) &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우의 자바바코드를 확인하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5vyoD/btrNOfbFN8h/FDXEPDXmfgkqMgaDPls6qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5vyoD/btrNOfbFN8h/FDXEPDXmfgkqMgaDPls6qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5vyoD/btrNOfbFN8h/FDXEPDXmfgkqMgaDPls6qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5vyoD%2FbtrNOfbFN8h%2FFDXEPDXmfgkqMgaDPls6qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;116&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 명시한 것과 같이, 다수의 @Target 이 존재하는 경우에는 &lt;code&gt;param&lt;/code&gt;, property, field 순으로 적용되므로 변환된 자바코드의 생성자 파라미터에 어노테이션이 붙어있는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;field.getAnnotations(Doc.class)&lt;/code&gt; 의 부분에서 null이 반환 되었고, 문서화 테스트케이스는 당연히 에러가 발생하였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수정 후&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use-site target을 사용하여 다음과 같이 변경하여 주면 정상 동작하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;data class ReceiverResponse(@field:Doc(desc = &quot;수령자 수&quot;, example = &quot;&quot;)
                            val receiverCount: Long,
                            @field:Doc(desc = &quot;수령자 정보&quot;, example = &quot;&quot;, child = true)
                            val receivers: List&amp;lt;Receiver&amp;gt;) &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정 한 후의 자바코드:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 필드에 원래의 의도대로 @Doc 어노테이션이 붙어있는 것을 확인 할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXokWB/btrNRDvVdVD/ShnVzNh68VbOzrhH9KD290/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXokWB/btrNRDvVdVD/ShnVzNh68VbOzrhH9KD290/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXokWB/btrNRDvVdVD/ShnVzNh68VbOzrhH9KD290/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXokWB%2FbtrNRDvVdVD%2FShnVzNh68VbOzrhH9KD290%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;286&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;에러도 해결!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교훈: 코틀린을 제대로 알고 사용하자&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;a href=&quot;https://kotlinlang.org/docs/reference/annotations.html&quot;&gt;https://kotlinlang.org/docs/reference/annotations.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;코틀린 인 액션 책&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BE</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/125</guid>
      <comments>https://yay-dev.tistory.com/entry/Kotlin-Annotation-Targets-1#entry125comment</comments>
      <pubDate>Wed, 5 Oct 2022 14:00:39 +0900</pubDate>
    </item>
    <item>
      <title>[nginx] 서비스 점검공지 올리는 방법</title>
      <link>https://yay-dev.tistory.com/entry/nginx-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%90%EA%B2%80%EA%B3%B5%EC%A7%80-%EC%98%AC%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2019-03-04-nginx-점검-페이지.md 라는 이름으로 github.io 블로그에 올렸던 내용을 가져온 내용입니다.&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;nginx 에서 간단하게 503 에러코드를 사용하여 점검페이지를 띄워보자&lt;/p&gt;
&lt;!--more--&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;li&gt;무중단이 불가능한 빅 업데이트&lt;br /&gt;내가 속한 팀에서는 주로 위와 같은 사유로 점검페이지를 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx 를 L4 뒤, application 앞에 두는 구조이기 때문에 점검 페이지를 nginx 쪽에서 관리하고있음.&lt;br /&gt;실제 점검 페이지는 html 파일로, 마크업/디자인 팀에서 전달받은 파일을 nginx 서버에 업로드 하여 사용함.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nginx 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx에서 점검 페이지를 관리하려면 conf 파일에 수정을 해야함.&lt;br /&gt;http status code 503 을 사용하여 에러 페이지로 redirect 하도록 다음과 같이 설정.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;server {
    ...
    location / {
        error_page 503 /maintenance_on.html;
        if (-f [서비스경로]/maintenance_on.html) {
            return 503;
        }
        ...
    }
    # Error pages.  
    location = /maintenance_on.html {
        root [서비스경로];
    }
    ...
}&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;점검 페이지가 사용되지 않을때는 maintenance_off.html 로 파일명을 유지.&lt;/li&gt;
&lt;li&gt;점검페이지를 띄우는 스크립트 (maintenance.py) 가 maintenance_on.html 로 점검페이지 파일명 변경.&lt;/li&gt;
&lt;li&gt;maintenance_on.html 파일이 존재하는 경우 503 error code 를 반환.&lt;/li&gt;
&lt;li&gt;503 error code를 maintenance_on.html 로 매핑.&lt;/li&gt;
&lt;li&gt;요청이 /maintenance_on.html 로 들어오는 경우, 지정된 경로의 파일로 요청 redirect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;점검페이지 ON/OFF 스크립트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 설정을 빠르게 하기위해서 간단한 스크립트를 작성하였다.&lt;br /&gt;사용 방법은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python3 maintenance.py [서비스명] [on or off]&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;'''
$&amp;gt; python3 maintenance.py [app_name] [on/off]

status = [on, off]
app_name = [서비스명]
@author yeji im
'''
import sys, os, time, os.path

CONSTRUCTION_BASE=&quot;[maintenance file path]&quot;

def construction_site():
    app_name = sys.argv[1]
    status = sys.argv[2]
    CONSTRUCTION_BASE =  CONSTRUCTION_BASE + app_name + &quot;/&quot;

    new_file_name = &quot;maintenance_&quot; + status + &quot;.html&quot;
    old_file_name = &quot;maintenance_&quot;;

    if status == &quot;off&quot;:
        old_file_name = old_file_name + &quot;on.html&quot;
    else:
        old_file_name = old_file_name + &quot;off.html&quot;

    # change maintenance file
    os.rename(CONSTRUCTION_BASE + old_file_name, CONSTRUCTION_BASE + new_file_name)

construction_site()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DevOps/nginx</category>
      <category>503</category>
      <category>maintenance</category>
      <category>maintenance page</category>
      <category>NGINX</category>
      <category>nginx 503</category>
      <category>서비스 점검공지</category>
      <category>점검페이지</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/122</guid>
      <comments>https://yay-dev.tistory.com/entry/nginx-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%90%EA%B2%80%EA%B3%B5%EC%A7%80-%EC%98%AC%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95#entry122comment</comments>
      <pubDate>Wed, 5 Oct 2022 13:53:53 +0900</pubDate>
    </item>
    <item>
      <title>Transactional + DDD</title>
      <link>https://yay-dev.tistory.com/entry/Transactional-DDD</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2019년 12월 16일에 notion에 작성한 글을 옮겨 왔다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@Transactional + DDD&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Pr스샷.png&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLfqF/btrNNozqm9n/WGIrkhkqRC0re87S9sO3m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLfqF/btrNNozqm9n/WGIrkhkqRC0re87S9sO3m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLfqF/btrNNozqm9n/WGIrkhkqRC0re87S9sO3m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLfqF%2FbtrNNozqm9n%2FWGIrkhkqRC0re87S9sO3m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;587&quot; data-filename=&quot;Pr스샷.png&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;587&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1664544267461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun requestRefund(request: RefundRequest): RefundResponse {
    val paymentInfo = 결제클라이언트.get결제정보(request.orderNo)
    return refund(refundDomainService.register(request, paymentInfo))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 외부의 호출이 일어날떄 transaction을 분리하는 경우는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transaction이 이미 열렸고, 외부 호출을 하는 경우 timeout 같은 에러 발생 시, 지연된 시간만큼 transaction을 물고 있기 때문에 좋지 않다. &amp;rarr; db lock 발생 가능 &lt;span style=&quot;background-color: #c1bef9;&quot;&gt;(그런데 db lock은 정확히 언제 발생하는가?  )&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB transaction의 시작은 refundDomainService.register 를 호출하였을때 시작이 된다. (정확하게 언제 시작되는지에 대해서 잘 모르겠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;register() 호출 이전에 transaction을 사용하고 있는 명령이 없기 떄문에, 해당 @Transactional 은 Application layer에 있는 것이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이유 이외에도 Application layer에 두는 이유는 다음과 같다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DomainService layer에 있는 경우, 해당 function을 다른 곳에서 호출 하였을 떄 원하는 동작이 일어나지 않을 수도 있다. (ex. for loop으로 여러 컬렉션을 register 하게 되는 경우)&lt;/li&gt;
&lt;li&gt;위에 언급한 것과 같이 register() 이전에 transaction을 여는 다른 명령어가 있는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 domain function을 호출하는 곳에서 (Application layer) Transaction을 관리하는 것이 좀 더 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction이 이미 시작되었고 &amp;rarr;외부 호출 (feign client) &amp;rarr; register 로 만약에 코드가 짜여졌다면, transaction을 분리해서 가져가는 설계가 훨씬 맞다.&lt;/p&gt;</description>
      <category>devlog/TIL</category>
      <category>ddd</category>
      <category>Kotlin</category>
      <category>Spring</category>
      <category>til</category>
      <category>transactional</category>
      <category>설계고민</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/121</guid>
      <comments>https://yay-dev.tistory.com/entry/Transactional-DDD#entry121comment</comments>
      <pubDate>Fri, 30 Sep 2022 22:25:17 +0900</pubDate>
    </item>
    <item>
      <title>[리팩토링] 에러로그 리팩토링에 대한 개발자1의 의식의 흐름</title>
      <link>https://yay-dev.tistory.com/entry/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%97%90%EB%9F%AC%EB%A1%9C%EA%B7%B8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EB%B0%9C%EC%9E%901%EC%9D%98-%EC%9D%98%EC%8B%9D%EC%9D%98-%ED%9D%90%EB%A6%84</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2019-04-11-errorlog-refactoring-devlog.md 라는 이름으로 이전 github페이지 블로그에 올렸던 글을 가져온 내용입니다.&amp;nbsp;&lt;br /&gt;2019년도 당시에 개발했던 내용에 대한 고민을 회고방식으로 남겼습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;intro&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주문,&amp;nbsp;결제&amp;nbsp;관련&amp;nbsp;개발을&amp;nbsp;하는&amp;nbsp;파트에서&amp;nbsp;일하고&amp;nbsp;있는&amp;nbsp;개발자1입니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;주된 업무를 마치고 잠시 쉬어가는 타이밍에 side task 정도로 기존의 에러로그 리팩토링을 진행하였는데, 간단한 업무였지만 나름의 (?!) 고민을 이것저것 하였습니다. 약간 생각의 흐름대로 작성한 리팩토링 고민 입니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무를&amp;nbsp;요약하자면&amp;nbsp;다음과&amp;nbsp;같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;결제 관련한 에러로그를 찍을 때, 주문/결제관련 request model, 결제키를 가진 model 등 개인정보에 민감한 주문자에 대한 정보를 담고있는 객체들을 전체 출력하고 있음 (전부 toString()을 사용하여 출력). 에러 debugging 시 정말 필요한 필드만 에러로그에 남기는 방향으로 리팩토링 해야함.&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;이것은&amp;nbsp;기존의&amp;nbsp;코드!&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1664541378989&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected 정의한Exception loggingErrorAndWrapping정의한Exception(Exception e, 정의한결제errorCode errorCode,
                                                                   Object... args) {
    if (e instanceof 정의한Exception) {
        return (정의한Exception)e;
    } else {
        String argsStr = &quot;&quot;;
        for (Object arg : args) {
            if (arg != null) {
                argsStr += arg.getClass().getName() + &quot;: &quot; + arg + &quot;\n&quot;;
            }
        }
        log.error(&quot;============= 결제 에러로그 =============\n&quot; + argsStr, e);
        return new 정의한Exception(errorCode);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/h4&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;toString()을&amp;nbsp;수정하면&amp;nbsp;되지&amp;nbsp;않을까?&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른&amp;nbsp;개발자분에게&amp;nbsp;의견을&amp;nbsp;드렸더니,&amp;nbsp;그것보다는&amp;nbsp;parameter로&amp;nbsp;넘기는것으로&amp;nbsp;수정해봐라~&amp;nbsp;라고&amp;nbsp;하시니,&amp;nbsp;일단은&amp;nbsp;그&amp;nbsp;방법은&amp;nbsp;접어두었다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;뭔가&amp;nbsp;문제가&amp;nbsp;있어서&amp;nbsp;그런거겠지&amp;nbsp;라는&amp;nbsp;생각으로.&lt;br /&gt;그리고&amp;nbsp;이미&amp;nbsp;toString()을&amp;nbsp;열렬하게&amp;nbsp;사용하는&amp;nbsp;곳이&amp;nbsp;있다면?!&amp;nbsp;그걸&amp;nbsp;다&amp;nbsp;일일히&amp;nbsp;찾아서&amp;nbsp;수정할&amp;nbsp;수도&amp;nbsp;없는&amp;nbsp;노릇이고.&amp;nbsp;&amp;nbsp;&lt;br /&gt;다음&amp;nbsp;글들은&amp;nbsp;생각의&amp;nbsp;흐름대로&amp;nbsp;정렬한&amp;nbsp;semi-해결방안&amp;nbsp;들이다.&amp;nbsp;개발자1의&amp;nbsp;의식의&amp;nbsp;흐름으로...!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;parameter로&amp;nbsp;필요한&amp;nbsp;필드를&amp;nbsp;넘겨준다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은&amp;nbsp;추천받은&amp;nbsp;방법대로&amp;nbsp;해봐야&amp;nbsp;하지&amp;nbsp;않겠나.&amp;nbsp;당연히&amp;nbsp;해봤다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;PayErrorInfo&lt;/span&gt; 라는 클래스를 생성해서 클래스명, 클래스에서 출력하고자 하는 필드로 생성.&lt;/p&gt;
&lt;pre id=&quot;code_1664541511534&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PayErrorInfo {
    private String className;
    private List&amp;lt;String&amp;gt; fields; 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드를&amp;nbsp;호출하는&amp;nbsp;부분에서는&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;사용&lt;/p&gt;
&lt;pre id=&quot;code_1664541525940&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; fields = ArrayLists.newArrayList();
fields.add(주문번호);
정의한Exception exception = loggingErrorAndWrapping정의한Exception(e, 정의한결제errorCode.ERROR_CODE, new PayErrorInfo(주문model.getClass().getName(), fields));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만..&lt;br /&gt;기존에는&amp;nbsp;손쉽게&amp;nbsp;object를&amp;nbsp;넘기기만&amp;nbsp;하면&amp;nbsp;끝이었는데,&amp;nbsp;지금은&amp;nbsp;불필요한&amp;nbsp;코드가&amp;nbsp;늘어난&amp;nbsp;느낌.&amp;nbsp;&lt;br /&gt;깔끔하게&amp;nbsp;리팩토링&amp;nbsp;하려는&amp;nbsp;나의&amp;nbsp;의도와는&amp;nbsp;전혀&amp;nbsp;다른&amp;nbsp;결과가&amp;nbsp;나옴!&amp;nbsp;&amp;nbsp;&lt;br /&gt;이것보다 더 나은 방법은 없을까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;Annotation&amp;nbsp;으로&amp;nbsp;해결&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;annotation으로&amp;nbsp;각&amp;nbsp;model,&amp;nbsp;entity에서&amp;nbsp;에러로&amp;nbsp;찍고자&amp;nbsp;하는&amp;nbsp;필드에&amp;nbsp;사용하면&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;깔끔하게&amp;nbsp;해결할&amp;nbsp;수&amp;nbsp;있지&amp;nbsp;않을까?&lt;/p&gt;
&lt;pre id=&quot;code_1664541579591&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 에러로그에 포함하고 싶은 필드
 * @author Yeji Im
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PaymentLog {}&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;@PaymentLog를&amp;nbsp;사용하는&amp;nbsp;객체&lt;/p&gt;
&lt;pre id=&quot;code_1664541596418&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class 주문객체 {

    @PaymentLog
    private String 주문번호;
    ...
}&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;@PaymentLog&amp;nbsp;어노테이션이&amp;nbsp;있는&amp;nbsp;필드만&amp;nbsp;출력하게끔&amp;nbsp;PayErrorInfo&amp;nbsp;수정!&lt;/p&gt;
&lt;pre id=&quot;code_1664541612937&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PayErrorInfo {
    private Object object;
    
    public PayErrorInfo(Object object) {
        this.object = object;
    }
    
    public String toString() {
        Class&amp;lt;?&amp;gt; clazz = object.getClass();
        Map&amp;lt;String, String&amp;gt; map = Maps.newHashMap();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(PaymentLog.class)) {
                try {
                    map.put(field.getName(), field.get(object).toString());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return map.entrySet()
                  .stream()
                  .map(entry -&amp;gt; &quot;[&quot; + entry.getKey() + &quot;=&quot; + entry.getValue() + &quot;]&quot;)
                  .collect(Collectors.joining(&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;메소드를&amp;nbsp;사용하는&amp;nbsp;부분!&amp;nbsp;이전보다는&amp;nbsp;간결&amp;nbsp;해졌다.&lt;/p&gt;
&lt;pre id=&quot;code_1664541626209&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;throw loggingErrorAndWrapping정의한Exception(e,정의한결제errorCode.ERROR_CODE,new PayErrorInfo(결제request))&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;사용해서&amp;nbsp;best&amp;nbsp;case&amp;nbsp;에는&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;출력&amp;nbsp;됨.&lt;/p&gt;
&lt;pre id=&quot;code_1664541642726&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[개발자1의프로젝트] : [ERROR] 2019-04-09 14:20:32,734 [main] 이런저런패키지.결제Service 
============= 결제 에러로그 =============
[주문번호=12341234]&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;원래 목적이었던 몇몇의(주문번호, 주문서번호)&amp;nbsp;&amp;nbsp;단일필드를 성공적으로 출력!&lt;/li&gt;
&lt;li&gt;직전의 방법보다 깔끔함.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;하지만..&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하게 된다면, 다음과 같이 개발자1의 의도와 다르게 동작할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collection 인 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;상품&amp;gt; 과 같은 collection 의&amp;nbsp;&amp;nbsp;내부에서 @PaymentLog를 사용한다면? =&amp;gt; 정상적으로 동작 X&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1664541754037&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;   public class 상품 {
        @PaymentLog
        private String 상품명;
    }&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;span style=&quot;background-color: #c1bef9;&quot;&gt;field.get(object)&lt;/span&gt; 처럼 직접적으로 access 하는 경우에, 해당 필드의 getter가 override된 상황이라면? 원래 getter로 받으면 정상적인 값을 받지만 직접 access 하게 된다면 &lt;b&gt;전혀 다른 값을 사용하게 될 가능성&lt;/b&gt;이 있음.&lt;/li&gt;
&lt;li&gt;reflection을 사용하였을 때 &lt;b&gt;성능&lt;/b&gt;에 대한 문제&lt;/li&gt;
&lt;li&gt;나의 의도와는 다르게 (단건에 대한 필드에만 동작, 정해진 몇몇 필드만 사용할 예정) 시간이 지나서 다른 개발자가 사용하게 된다면? =&amp;gt; &lt;b&gt;에러 발생 확률 높아짐&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와같은&amp;nbsp;이유로&amp;nbsp;다른&amp;nbsp;방법을&amp;nbsp;고려하게&amp;nbsp;됨.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;Visitor Pattern&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀원분이 Visitor pattern 적용해보는 것을 추천해주셨다.&amp;nbsp;참고:&amp;nbsp;&lt;a href=&quot;https://sourcemaking.com/design_patterns/visitor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sourcemaking.com/design_patterns/visitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;로그를 찍고자 하는 클래스가 추가될 때마다 VisitorImpl에 메소드가 추가 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;로그를&amp;nbsp;찍는&amp;nbsp;사소하지만&amp;nbsp;공통적인&amp;nbsp;작업을&amp;nbsp;한&amp;nbsp;클래스&amp;nbsp;내부에서&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;결제Service&amp;nbsp;내부의&amp;nbsp;몇&amp;nbsp;메소드에서만&amp;nbsp;사용하는&amp;nbsp;로깅용&amp;nbsp;method&amp;nbsp;인데&amp;nbsp;굳이&amp;nbsp;이렇게&amp;nbsp;적용할&amp;nbsp;필요가&amp;nbsp;있을까?&amp;nbsp;주문&amp;nbsp;도메인&amp;nbsp;전체에서&amp;nbsp;사용하는&amp;nbsp;공통적인&amp;nbsp;에러로그&amp;nbsp;작업이라면&amp;nbsp;고려해&amp;nbsp;볼만&amp;nbsp;할&amp;nbsp;것&amp;nbsp;같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다른 방법을 다시 고민해 보았다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;toString()&amp;nbsp;같은&amp;nbsp;메소드&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국에는&amp;nbsp;다시&amp;nbsp;돌아와서&amp;nbsp;toString().&lt;br /&gt;아무리 생각해도 &lt;b&gt;에러 확률도 가장 적고&lt;/b&gt;, 기존에 있는 코드를 수정하지 않는 선에서 할 수 있는 최선의 방법이라고 생각하였다. (처음부터 이렇게 할걸...)&amp;nbsp;&amp;nbsp;&lt;br /&gt;에러로깅메소드를 사용하는 몇 클래스/request model에서는 이미 toString() 을 override해서&amp;nbsp;&amp;nbsp;사용하고 있어서 구분짓기 위해&lt;span style=&quot;background-color: #c1bef9;&quot;&gt; toPaymentErrorLog()&lt;/span&gt; 라는 메소드를 추가하였다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;그리고&amp;nbsp;지금까지&amp;nbsp;별로&amp;nbsp;중요하게&amp;nbsp;생각하지&amp;nbsp;않았던&amp;nbsp;부분도&amp;nbsp;있었다.&amp;nbsp;바로&amp;nbsp;method의&amp;nbsp;이름!!&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;method의&amp;nbsp;이름&amp;nbsp;재&amp;nbsp;정의&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;loggingErrorAndWrapping정의한Exception&lt;/span&gt;&lt;br /&gt;메소드&amp;nbsp;명만&amp;nbsp;봤을때는&amp;nbsp;해당&amp;nbsp;메소드의&amp;nbsp;용도는&amp;nbsp;이해가&amp;nbsp;된다.&amp;nbsp;에러를&amp;nbsp;로그로&amp;nbsp;남기고&amp;nbsp;정의한&amp;nbsp;exception을&amp;nbsp;wrapping해서&amp;nbsp;반환한다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;하지만 너무 길다. (물론 긴 메소드명이 틀렸다는 것은 아니지만, 더 명확하지만 짧은 메소드명을 선호한다!)&amp;nbsp;&amp;nbsp;&lt;br /&gt;그리고&amp;nbsp;길다고&amp;nbsp;생각해서&amp;nbsp;변경하려&amp;nbsp;하니&amp;nbsp;또&amp;nbsp;마땅히&amp;nbsp;생각나는&amp;nbsp;메소드&amp;nbsp;명은&amp;nbsp;없었다.&amp;nbsp;&lt;br /&gt;그렇다면 이 메소드는 한가지 일을 하지 않는 메소드 (And을 보아 짐작할 수 있다) 여서 어려운 것이라고 생각했다.&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;&lt;b&gt;더&amp;nbsp;좋은&amp;nbsp;메소드명으로&amp;nbsp;변경하려면&amp;nbsp;기능을&amp;nbsp;분리해야한다!&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;정의한 exception을 wrapping 하는 곳&amp;nbsp;&lt;/li&gt;
&lt;li&gt;에러로그를 남기는 곳&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;변경&amp;nbsp;하였다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1664542146807&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * 정의한ErrorCode를 담은 정의한Exception을 반환
 */
protected 정의한Exception generate정의한Exception(Exception e, 정의한결제errorCode errorCode, Object... args) {
    if (e instanceof 정의한Exception) {
        return (정의한Exception)e;
    } else {
        print결제에러(e, args);
        return new 정의한Exception(errorCode);
    }
}

private void print결제에러(Exception e, Object... args) {
    String argsStr = &quot;&quot;;
    for (Object arg : args) {
        if (arg != null) {
            argsStr += arg.getClass().getName() + &quot;: &quot;;
            Optional&amp;lt;Method&amp;gt; method = Optional.ofNullable(ReflectionUtils.findMethod(arg.getClass(),
                                                                                     &quot;toPaymentErrorLog&quot;));
            if (method.isPresent()) {
                argsStr += ReflectionUtils.invokeMethod(method.get(), arg) + &quot;\\n&quot;;
            } else {
                argsStr += arg + &quot;\\n&quot;;
            }
        }
    }
    log.error(&quot;============= 결제 에러로그 =============\\n&quot; + argsStr, e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존의 method를 기능별로 분리하고 명명하였다.&lt;/li&gt;
&lt;li&gt;toPayErrorlog가 있는 클래스는 그 포맷으로 출력하고 없는 경우에는 toString 으로 클래스의 내용을 출력하는 방향으로 수정하였다.&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로&amp;nbsp;남긴&amp;nbsp;코드가&amp;nbsp;메소드&amp;nbsp;리팩토링&amp;nbsp;진짜_마지막_리얼_마지막_코드.java&amp;nbsp;이다.&amp;nbsp;이것보다&amp;nbsp;더&amp;nbsp;좋은&amp;nbsp;방법이&amp;nbsp;물론&amp;nbsp;있으리라&amp;nbsp;믿는다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/BE</category>
      <category>java</category>
      <category>리팩토링</category>
      <category>리팩토링 고민</category>
      <category>삽질</category>
      <category>자바</category>
      <category>코드리팩토링</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/120</guid>
      <comments>https://yay-dev.tistory.com/entry/%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%97%90%EB%9F%AC%EB%A1%9C%EA%B7%B8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EB%B0%9C%EC%9E%901%EC%9D%98-%EC%9D%98%EC%8B%9D%EC%9D%98-%ED%9D%90%EB%A6%84#entry120comment</comments>
      <pubDate>Fri, 30 Sep 2022 21:56:55 +0900</pubDate>
    </item>
    <item>
      <title>[성능] heap dump 분석 툴 / heap dump analyzer</title>
      <link>https://yay-dev.tistory.com/entry/%EC%84%B1%EB%8A%A5-heap-dump-%EB%B6%84%EC%84%9D-%ED%88%B4-heap-dump-analyzer</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 필요 X / 10mb 파일 사이즈 제한 있음&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Heap Hero&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://heaphero.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://heaphero.io/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663647190569&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;World-class heap Dump analysis - Java, Android memory dump analyzer&quot; data-og-description=&quot;How much memory your application wastes? Due to inefficient programming, modern applications waste 30% to 70% of memory. HeapHero is the industry's first tool to detect the amount of wasted memory. It reports what lines of source code originating the memor&quot; data-og-host=&quot;heaphero.io&quot; data-og-source-url=&quot;https://heaphero.io/&quot; data-og-url=&quot;https://heaphero.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jDbN5/hyPQ3xVUWj/QQ1eA72hSShui1qfjlRwRk/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://heaphero.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://heaphero.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jDbN5/hyPQ3xVUWj/QQ1eA72hSShui1qfjlRwRk/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&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;World-class heap Dump analysis - Java, Android memory dump analyzer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How much memory your application wastes? Due to inefficient programming, modern applications waste 30% to 70% of memory. HeapHero is the industry's first tool to detect the amount of wasted memory. It reports what lines of source code originating the memor&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;heaphero.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST API도 있다! 사이즈 제한이 있는것이 좀 흠이긴 하지만, 이걸로 heap dump 분석 -&amp;gt; 애용하는 messenger로 바로 분석된 결과물을 볼 수 있도록 자동화 하는 방법도 좋을듯!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1663647351779&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;HEAP DUMP ANALYSIS API&quot; data-og-description=&quot;Android, JVM Heap dump analysis doesn&amp;rsquo;t have to be done manually (painfully) anymore. You can programmatically analyze Heap dumps through our REST API. Below are the few use cases where our heap du&amp;hellip;&quot; data-og-host=&quot;blog.heaphero.io&quot; data-og-source-url=&quot;https://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&quot; data-og-url=&quot;http://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GMXV0/hyPQ0HYnqC/a7psA8YXKCM06FCma4aox1/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311,https://scrap.kakaocdn.net/dn/bJbeuA/hyPQ1GSriS/TK60XsVkL0uiJCbXgW6Yf1/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311,https://scrap.kakaocdn.net/dn/EKQZl/hyPQo4LiCO/HjEzvrR7KzVhuIZCtVfju0/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311&quot;&gt;&lt;a href=&quot;https://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.heaphero.io/2018/06/22/heap-dump-analysis-api/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GMXV0/hyPQ0HYnqC/a7psA8YXKCM06FCma4aox1/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311,https://scrap.kakaocdn.net/dn/bJbeuA/hyPQ1GSriS/TK60XsVkL0uiJCbXgW6Yf1/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311,https://scrap.kakaocdn.net/dn/EKQZl/hyPQo4LiCO/HjEzvrR7KzVhuIZCtVfju0/img.png?width=537&amp;amp;height=311&amp;amp;face=0_0_537_311');&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;HEAP DUMP ANALYSIS API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Android, JVM Heap dump analysis doesn&amp;rsquo;t have to be done manually (painfully) anymore. You can programmatically analyze Heap dumps through our REST API. Below are the few use cases where our heap du&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.heaphero.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fastthread&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fastthread.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastthread.io/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663647381320&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;fastthread.io&quot; data-og-description=&quot;Thread Dump Analysis REST API In this modern world, thread dumps are still analyzed in a tedious &amp;amp; manual mode. i.e., Operations engineer captures thread dumps from the production servers; then he transmits those files to developers. Developers use thread &quot; data-og-host=&quot;fastthread.io&quot; data-og-source-url=&quot;https://fastthread.io/&quot; data-og-url=&quot;http://fastthread.io/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://fastthread.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastthread.io/&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;fastthread.io&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Thread Dump Analysis REST API In this modern world, thread dumps are still analyzed in a tedious &amp;amp; manual mode. i.e., Operations engineer captures thread dumps from the production servers; then he transmits those files to developers. Developers use thread&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastthread.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 필요 O&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;IntelliJ (유료버전)&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663647424831&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;Create and open memory snapshots | IntelliJ&amp;nbsp;IDEA&quot; data-og-description=&quot; &quot; data-og-host=&quot;www.jetbrains.com&quot; data-og-source-url=&quot;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&quot; data-og-url=&quot;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kQnBL/hyPQ2y01H7/yyxOmlTh3R2J5KCDGTLnqk/img.png?width=1281&amp;amp;height=800&amp;amp;face=0_0_1281_800,https://scrap.kakaocdn.net/dn/cdIMhc/hyPRe0wm1i/jFARlVh5L8xK1ocDHMgtn1/img.png?width=1504&amp;amp;height=482&amp;amp;face=0_0_1504_482&quot;&gt;&lt;a href=&quot;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jetbrains.com/help/idea/create-a-memory-snapshot.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kQnBL/hyPQ2y01H7/yyxOmlTh3R2J5KCDGTLnqk/img.png?width=1281&amp;amp;height=800&amp;amp;face=0_0_1281_800,https://scrap.kakaocdn.net/dn/cdIMhc/hyPRe0wm1i/jFARlVh5L8xK1ocDHMgtn1/img.png?width=1504&amp;amp;height=482&amp;amp;face=0_0_1504_482');&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;Create and open memory snapshots | IntelliJ&amp;nbsp;IDEA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jetbrains.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 사용하고있는 IDE 툴에서 바로 분석도 가능! 하지만 MAT에 익숙한 사용자라면, 뭔가 부족함을 느낄수도..&lt;/li&gt;
&lt;li&gt;유료 버전만 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Memory Analyzer (MAT)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.eclipse.org/mat/downloads.php&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.eclipse.org/mat/downloads.php&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663647508206&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;Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation&quot; data-og-description=&quot;The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks.&quot; data-og-host=&quot;www.eclipse.org&quot; data-og-source-url=&quot;https://www.eclipse.org/mat/downloads.php&quot; data-og-url=&quot;https://www.eclipse.org/mat/downloads.php&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ACRoq/hyPQ8lHQF2/NYIOpXTa2vzRsm6l3I4GXK/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/bmMlP0/hyPQ3kn3SK/57hYbCXK1D4KOCkvwPQqPk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://www.eclipse.org/mat/downloads.php&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.eclipse.org/mat/downloads.php&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ACRoq/hyPQ8lHQF2/NYIOpXTa2vzRsm6l3I4GXK/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/bmMlP0/hyPQ3kn3SK/57hYbCXK1D4KOCkvwPQqPk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&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;Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.eclipse.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mac도 지원함!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/devops</category>
      <category>heap dump</category>
      <category>heap dump analyzer</category>
      <category>IntelliJ 힙덤프</category>
      <category>java</category>
      <category>java oom</category>
      <category>mat</category>
      <category>oom</category>
      <category>힙덤프</category>
      <category>힙덤프 분석</category>
      <category>힙덤프 분석 툴</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/116</guid>
      <comments>https://yay-dev.tistory.com/entry/%EC%84%B1%EB%8A%A5-heap-dump-%EB%B6%84%EC%84%9D-%ED%88%B4-heap-dump-analyzer#entry116comment</comments>
      <pubDate>Tue, 20 Sep 2022 13:21:12 +0900</pubDate>
    </item>
    <item>
      <title>[Gradle] gradle 빌드 OOM 발생할땐?</title>
      <link>https://yay-dev.tistory.com/entry/Gradle-gradle-%EB%B9%8C%EB%93%9C-OOM-%EB%B0%9C%EC%83%9D%ED%95%A0%EB%95%90</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CI/CD 단계중 빌드단계에서 OOM이 발생하는 경우가 얼마나 될까?&amp;nbsp;&lt;br /&gt;일단 지금까지 여러 어플리케이션의 빌드를 돌려봤지만 처음 경험해 봤다.  &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젠킨스 빌드 로그에는 아래와 같이 OutofMemoryError가 발생했다는 로그가 남아있다. 그리고 힙덤프 업로드 알림이 왔다.&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/dd2EhF/btrMzD4TYTf/cpodhj5xgevsQj4fpEpH51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dd2EhF/btrMzD4TYTf/cpodhj5xgevsQj4fpEpH51/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;49&quot; data-filename=&quot;edited_edited_스크린샷 2022-09-20 오후 12.55.58.png&quot; data-widthpercent=&quot;77.42&quot; style=&quot;width: 76.5191297824456%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dd2EhF/btrMzD4TYTf/cpodhj5xgevsQj4fpEpH51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdd2EhF%2FbtrMzD4TYTf%2Fcpodhj5xgevsQj4fpEpH51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3E1uP/btrMyfXGWT3/0J2WAjh0Hg0WEvBXYoZu10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3E1uP/btrMyfXGWT3/0J2WAjh0Hg0WEvBXYoZu10/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;240&quot; data-filename=&quot;edited_스크린샷 2022-09-20 오후 12.51.38.png&quot; width=&quot;500&quot; height=&quot;241&quot; data-widthpercent=&quot;22.58&quot; style=&quot;width: 22.318079519879973%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3E1uP/btrMyfXGWT3/0J2WAjh0Hg0WEvBXYoZu10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3E1uP%2FbtrMyfXGWT3%2F0J2WAjh0Hg0WEvBXYoZu10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;240&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;내가 만든 OOM업로더 알림이 :)&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙덤프를 분석해보기 위해 MAT실행 &amp;gt; 분석!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-20 오후 12.59.24.png&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buoV1U/btrMA5NEWbU/D8FpCfPmCn0wF7RxkkJfqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buoV1U/btrMA5NEWbU/D8FpCfPmCn0wF7RxkkJfqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buoV1U/btrMA5NEWbU/D8FpCfPmCn0wF7RxkkJfqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuoV1U%2FbtrMA5NEWbU%2FD8FpCfPmCn0wF7RxkkJfqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;282&quot; data-filename=&quot;스크린샷 2022-09-20 오후 12.59.24.png&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 처음 보는 에러가 memory leak의 가장 의심되는 부분이라고 분석되었다. 원인을 위해 해당 검색을 해보았지만 딱히 도움되는 글을 발견하지는 못했다. (&lt;a title=&quot;가장 관련있어 보이는 stackoverflow의 글 일단 링크&quot; href=&quot;https://stackoverflow.com/questions/36743003/java-rmi-tcp-connect-memory-issue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;가장 관련있어 보이는 stackoverflow의 글 일단 링크&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;빌드 oom이니까 gradle의 문제아닐까..?&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 힙덤프를 봐도 모르겠으니, 다음 타겟인 gradle을 의심해봤다. 관련해서 자료를 찾아보니, gradle daemon 의 heap size가 부족하면 OOM이 발생할 수도 있다고 한다. 당장 바꿔~~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-20 오후 1.03.39.png&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zoags/btrMyYVPKR0/3qCEVkvOXAYxctWl9xfQH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zoags/btrMyYVPKR0/3qCEVkvOXAYxctWl9xfQH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zoags/btrMyYVPKR0/3qCEVkvOXAYxctWl9xfQH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzoags%2FbtrMyYVPKR0%2F3qCEVkvOXAYxctWl9xfQH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;242&quot; height=&quot;31&quot; data-filename=&quot;스크린샷 2022-09-20 오후 1.03.39.png&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;gradle.properties&lt;/span&gt; 파일에 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;jvmargs&lt;/span&gt; 옵션을 추가하고 다시 빌드를 돌려보니, 빌드가 성공적으로 돌았다!!!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 왜 이전에는 빌드에 실패헀을까? 따로 설정한 값이 없었으니, gradle daemon의 default 설정으로 실행되었을텐데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle daemon이 해당 프로젝트를 빌드할때 default size인 512MB보다 큰 값이 필요했던것으로 보인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니 512MB보다 더 필요한 경우는 처음보네..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 규모가 있는 어플리케이션의 경우에는 512MB보다 더 필요하다고 gradle 문서에 써있지만, 해당 어플리케이션은 그렇게 규모가 크지는 않은데..? 규모가 큰것에 대한 기준이 뭐지?  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;좀 더 알아볼 부분&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gradle daemon, gradle launcher&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고한 문서&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://sup2is.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sup2is.tistory.com/115&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gradle.org/current/userguide/gradle_daemon.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.gradle.org/current/userguide/gradle_daemon.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>devlog/TIL</category>
      <category>Gradle</category>
      <category>gradle daemon</category>
      <category>gradle heap size</category>
      <category>gradle oom</category>
      <category>gradle 빌드</category>
      <category>빌드 oom</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/115</guid>
      <comments>https://yay-dev.tistory.com/entry/Gradle-gradle-%EB%B9%8C%EB%93%9C-OOM-%EB%B0%9C%EC%83%9D%ED%95%A0%EB%95%90#entry115comment</comments>
      <pubDate>Tue, 20 Sep 2022 13:10:26 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] java.lang.NoSuchMethodError: No such DSL method 'cleanWs' found among steps 에러 해결 방법</title>
      <link>https://yay-dev.tistory.com/entry/Jenkins-javalangNoSuchMethodError-No-such-DSL-method-cleanWs-found-among-steps-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;새로운 Jenkins 인스턴스에서 기존 pipeline 코드 실행하다가 맞이한 에러&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 코드를 살펴보면 아래와 같은 형태로 나오는데,&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663214194669&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.NoSuchMethodError: No such DSL method 'cleanWs' found among steps [...] or symbols [...] or globals [...]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;steps에도 없고, symbols에도 없고, 따로 지정한 globals에도 없으니 이건 플러그인 미설치때문에 발생한 에러이다.&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;&lt;a href=&quot;https://plugins.jenkins.io/ws-cleanup/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://plugins.jenkins.io/ws-cleanup/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1663214305661&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Workspace Cleanup&quot; data-og-description=&quot;This plugin deletes the project workspace when invoked.&quot; data-og-host=&quot;plugins.jenkins.io&quot; data-og-source-url=&quot;https://plugins.jenkins.io/ws-cleanup/&quot; data-og-url=&quot;https://plugins.jenkins.io/ws-cleanup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b5VThf/hyPM0wdHIf/r1ElDWEWYctKsOdKBenNck/img.png?width=796&amp;amp;height=398&amp;amp;face=80_109_160_197,https://scrap.kakaocdn.net/dn/bFoXUB/hyPN1twkAP/QCxgm2pk8gXSMhCXYbk2j1/img.png?width=796&amp;amp;height=398&amp;amp;face=80_109_160_197&quot;&gt;&lt;a href=&quot;https://plugins.jenkins.io/ws-cleanup/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plugins.jenkins.io/ws-cleanup/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b5VThf/hyPM0wdHIf/r1ElDWEWYctKsOdKBenNck/img.png?width=796&amp;amp;height=398&amp;amp;face=80_109_160_197,https://scrap.kakaocdn.net/dn/bFoXUB/hyPN1twkAP/QCxgm2pk8gXSMhCXYbk2j1/img.png?width=796&amp;amp;height=398&amp;amp;face=80_109_160_197');&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;Workspace Cleanup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This plugin deletes the project workspace when invoked.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plugins.jenkins.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE</category>
      <category>jenkins</category>
      <category>jenkins no such method</category>
      <category>no such DSL method</category>
      <category>젠킨스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/113</guid>
      <comments>https://yay-dev.tistory.com/entry/Jenkins-javalangNoSuchMethodError-No-such-DSL-method-cleanWs-found-among-steps-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95#entry113comment</comments>
      <pubDate>Thu, 15 Sep 2022 13:03:09 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins 버전 업데이트 &amp;amp; 롤백하는 방법</title>
      <link>https://yay-dev.tistory.com/entry/Jenkins-%EB%B2%84%EC%A0%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%A1%A4%EB%B0%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;새로운 버전의 Jenkins가 나올때 마다 업데이트 해주면 좋겠지만...다들 귀찮은거 압니다.&amp;nbsp;&lt;br /&gt;docker로 운영하는 Jenkins 업데이트가 가~끔 필요할때 이렇게 하면 됩니다!&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTS버전 기준으로 원하는 war파일 버전을 찾아주세요 -&amp;gt; &lt;a href=&quot;https://www.jenkins.io/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jenkins.io/download/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1663129572912&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Jenkins download and deployment&quot; data-og-description=&quot;Jenkins download and deployment The Jenkins project produces two release lines: Stable (LTS) and regular (Weekly). Depending on your organization's needs, one may be preferred over the other. See the links below for more information and recommendations abo&quot; data-og-host=&quot;www.jenkins.io&quot; data-og-source-url=&quot;https://www.jenkins.io/download/&quot; data-og-url=&quot;https://www.jenkins.io/download/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WNIgf/hyPMWszLt1/kQdql58kekgDtfda6m7rp1/img.png?width=1260&amp;amp;height=423&amp;amp;face=0_0_1260_423,https://scrap.kakaocdn.net/dn/yWR9v/hyPM1gmARR/cRI3RYYZKuUUtQAlUk9TjK/img.png?width=811&amp;amp;height=335&amp;amp;face=0_0_811_335&quot;&gt;&lt;a href=&quot;https://www.jenkins.io/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jenkins.io/download/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WNIgf/hyPMWszLt1/kQdql58kekgDtfda6m7rp1/img.png?width=1260&amp;amp;height=423&amp;amp;face=0_0_1260_423,https://scrap.kakaocdn.net/dn/yWR9v/hyPM1gmARR/cRI3RYYZKuUUtQAlUk9TjK/img.png?width=811&amp;amp;height=335&amp;amp;face=0_0_811_335');&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;Jenkins download and deployment&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Jenkins download and deployment The Jenkins project produces two release lines: Stable (LTS) and regular (Weekly). Depending on your organization's needs, one may be preferred over the other. See the links below for more information and recommendations abo&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jenkins.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker container 내부에서 직접 다운로드 후 재시작 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1663128997504&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# docker container 진입 
docker exec -it jenkins /bin/bash 

# wget 으로 .war 파일 다운로드 
sudo wget [jenkins version .war file link] 

# .war파일 경로 이동 
sudo mv jenkins.war /usr/share/jenkins 

# 파일 권한 변경 (root로 되어있지 않은 경우) 
sudo chown root:root jenkins.war

# docker 재시작 
docker restart jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이건 본 포스팅과는 관련없는 내용이지만..Jenkins restart 할 때 (위와같이 업데이트 하는 경우에) 데이터가 초기화 되는 현상 겪어보신 분 계신가요?&amp;nbsp;&lt;br /&gt;plugin 호환성이 맞지 않아서 그런 현상이 발생하는 것 같은데, 다른 원인 찾으신 분 계신다면 댓글 부탁드립니다  &lt;/blockquote&gt;</description>
      <category>BE</category>
      <category>docker jenkins</category>
      <category>jenkins</category>
      <category>jenkins LTS</category>
      <category>jenkins update</category>
      <category>jenkins version</category>
      <category>젠킨스</category>
      <category>젠킨스 업데이트</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/112</guid>
      <comments>https://yay-dev.tistory.com/entry/Jenkins-%EB%B2%84%EC%A0%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%A1%A4%EB%B0%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry112comment</comments>
      <pubDate>Wed, 14 Sep 2022 13:30:51 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins X 란?</title>
      <link>https://yay-dev.tistory.com/entry/Jenkins-X-%EB%9E%80</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 환경에서 CI/CD 툴을 조사하던 중 Jenkins X 를 발견하게 되었다.&lt;br /&gt;널리널리 사용되는 Jenkins! 그런데 좀 더 발전된 버전인가? 어떤 점이 다른지 살펴보자  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Jenkins X?&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubernetes 환경에서 CI(=continuous integration) 과 CD(continuous deployment) 를 제공하는 툴&lt;/li&gt;
&lt;li&gt;오픈소스 프로젝트, 현재도 열심히 개발되고 있는 프로젝트!&lt;/li&gt;
&lt;li&gt;아쉽게도 웹이나 serverless하게 확인해볼수있는 demo는 없지만, 아래 kubecon 영상에서 demo확인 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uHe7R_iZSLU&amp;amp;t=976&quot;&gt;2018 kubecon Jenkins X demo&lt;/a&gt; (Jenkins x 뽕에 취해보세요)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;특징&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Automated CI/CD&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github에서 commit, push, PR을 통해 자연스러운 CI/CD 환경을 경험할 수 있음 (모든 git event에 따라 각각 다른 pipeline이 동작)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 web UI를 통해 (jenkins, Argo CD같은) 배포 버튼을 누르는 작업을 따로 하지 않아도 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Environments&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;777&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LVoqk/btrL4mpka10/h0STezxhd49aKceia3te60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LVoqk/btrL4mpka10/h0STezxhd49aKceia3te60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LVoqk/btrL4mpka10/h0STezxhd49aKceia3te60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLVoqk%2FbtrL4mpka10%2Fh0STezxhd49aKceia3te60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1156&quot; height=&quot;777&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;777&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;Devlopment, Staging, Production 환경을 제공&lt;/li&gt;
&lt;li&gt;kubernetes cluster의 namespace 하나가 Jenkins X에서 말하는 enviornment가 됨&lt;/li&gt;
&lt;li&gt;Single cluster의 경우 하나의 cluster안에서 development, staging, production의 배포 환경을 namespace로 구분&lt;/li&gt;
&lt;li&gt;multi-cluster도 지원함&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;team == environment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Preview Environments&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PR에서 반영한 코드를 테스트할 수 있는 preview 환경을 제공&lt;/li&gt;
&lt;li&gt;개발자가 작업한 feature가 master branch로 (merge 하고자 하는 branch) merge가 되었을때의 결과물을 샘플로 보여주는 환경&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgbgO9/btrL3DLPzBX/pZ9VpUmWvYGCq6qDWdfuMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgbgO9/btrL3DLPzBX/pZ9VpUmWvYGCq6qDWdfuMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgbgO9/btrL3DLPzBX/pZ9VpUmWvYGCq6qDWdfuMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgbgO9%2FbtrL3DLPzBX%2FpZ9VpUmWvYGCq6qDWdfuMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1247&quot; height=&quot;240&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;dev pod&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PR을 올리기 전 (preview environment를 사용하기 전), 로컬환경에서의 작업물을 테스트할 수 있는 환경을 제공&lt;/li&gt;
&lt;li&gt;로컬환경에서 CI/CD 환경에 구축되어있지 않더라도, 현재 작업하고있는 작업물을 dev pod에서 테스트 가능&lt;/li&gt;
&lt;li&gt;따로 배포를 하지 않고, 사용하고있는 IDE에서 코드 저장하기만 하면 dev pod로 sync&lt;/li&gt;
&lt;li&gt;내부적으로 &lt;a href=&quot;https://skaffold.dev/&quot;&gt;skaffold&lt;/a&gt; 라는 오픈소스를 사용해서 동작
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분은 Jenkins X를 사용하지 않고, 다른 오픈소스 툴을 사용하면 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tilt.dev/&quot;&gt;Tilt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://skaffold.dev/&quot;&gt;Skaffold&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://garden.io/&quot;&gt;Garden&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Azure/draft/&quot;&gt;Draft&lt;/a&gt; (archived 된 프로젝트..)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참고: &lt;a href=&quot;https://www.cloudbees.com/blog/exploring-jenkins-x-devpod&quot;&gt;https://www.cloudbees.com/blog/exploring-jenkins-x-devpod&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;고려사항&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS, GKE, Azure에 최적화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;on-premise보다는 위의 클라우드 provider에서 사용하는것을 권장&lt;/li&gt;
&lt;li&gt;on-premise로 사용하게 될 경우에는, &lt;a href=&quot;https://metallb.universe.tf&quot;&gt;Metal LB&lt;/a&gt;로 구성해야함 (클라우드를 사용하게 될 경우, 이 부분은 걱정하지 않아도 됨)&lt;/li&gt;
&lt;li&gt;여러모로 설정부분에서 신경써야 할 것이 많긴 해 보임&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BE</category>
      <category>cicd</category>
      <category>jenkins</category>
      <category>jenkins x</category>
      <category>jenkinsX</category>
      <category>kubernetes</category>
      <category>kubernetes cicd</category>
      <category>배포</category>
      <category>젠킨스</category>
      <category>젠킨스x</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/111</guid>
      <comments>https://yay-dev.tistory.com/entry/Jenkins-X-%EB%9E%80#entry111comment</comments>
      <pubDate>Wed, 14 Sep 2022 13:03:47 +0900</pubDate>
    </item>
    <item>
      <title>[Python] scp, paramiko 패키지 예제</title>
      <link>https://yay-dev.tistory.com/entry/python-scp-paramiko</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파일 업로드에 사용할 python 패키지를 찾다가 scp, paramiko를 사용하여 아래와 같이 개발함!&lt;/p&gt;
&lt;pre id=&quot;code_1629941763230&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from paramiko import SSHClient
from scp import SCPClient

def upload_files(target, fileList):
    scp = connect_to_server()   
    files = []
    for file in fileList:
        filename = file.rstrip()
        file_full_path = &quot;졀대/상대경로&quot;+ filename
        scp.put(files=filename, remote_path=file_full_path, recursive=True)

    # fileList to 
    # scp.put(files=files, remote_path=target_path, recursive=True)    

def connect_to_server():
    ssh = SSHClient()
    ssh.load_host_keys([ssh key 경로])
    ssh.connect([서버주소], 22, 'centos', key_filename=[pem파일 경로])
    scp = SCPClient(ssh.get_transport())
    return scp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;paramiko 문서&quot; href=&quot;https://www.paramiko.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;paramiko 문서&lt;/a&gt; / &lt;a href=&quot;https://pypi.org/project/scp/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;scp 문서&lt;/a&gt;&lt;/p&gt;</description>
      <category>devlog/TIL</category>
      <category>paramiko</category>
      <category>Python</category>
      <category>python paramiko example</category>
      <category>python paramiko 예제</category>
      <category>python scp example</category>
      <category>python scp 예제</category>
      <category>scp</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/109</guid>
      <comments>https://yay-dev.tistory.com/entry/python-scp-paramiko#entry109comment</comments>
      <pubDate>Thu, 26 Aug 2021 10:36:18 +0900</pubDate>
    </item>
    <item>
      <title>대학생때 만들었던 사진 객체로 스티커 (?) 만들기</title>
      <link>https://yay-dev.tistory.com/entry/%EB%8C%80%ED%95%99%EC%83%9D%EB%95%8C-%EB%A7%8C%EB%93%A4%EC%97%88%EB%8D%98-%EC%82%AC%EC%A7%84-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%8A%A4%ED%8B%B0%EC%BB%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>
            &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;하드드라이브 정리하다가 발굴해낸 프로젝트. 여담을 하자면 이 프로젝트는 팀플젝이었는데, 4명중 2명이 중국으로 귀국(이라쓰고 도망)가버려서 나머지 2명이 울면서 개발한 프로젝트이다..빤쓰런을 하려면 저 정도 스케일은 되어야 인정을 받을 수 있을 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 내용도 다 까먹고, MATLAB도 까먹고, 남은건 코드와 자료들 뿐이지만, 나름 재밌었다. Computational Photography 수업이었음.&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;a href=&quot;https://github.com/yayyz/emojify-objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/yayyz/emojify-objects&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1626589308585&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;yayyz/emojify-objects&quot; data-og-description=&quot;Contribute to yayyz/emojify-objects development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/yayyz/emojify-objects&quot; data-og-url=&quot;https://github.com/yayyz/emojify-objects&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qVZPA/hyKVapnafn/sW43cVnKmzsN2dqHEKdPQK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/yayyz/emojify-objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/yayyz/emojify-objects&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qVZPA/hyKVapnafn/sW43cVnKmzsN2dqHEKdPQK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;yayyz/emojify-objects&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to yayyz/emojify-objects development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>devlog/etc</category>
      <category>대학교</category>
      <category>팀플</category>
      <category>팀플날먹</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/104</guid>
      <comments>https://yay-dev.tistory.com/entry/%EB%8C%80%ED%95%99%EC%83%9D%EB%95%8C-%EB%A7%8C%EB%93%A4%EC%97%88%EB%8D%98-%EC%82%AC%EC%A7%84-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%8A%A4%ED%8B%B0%EC%BB%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry104comment</comments>
      <pubDate>Sun, 18 Jul 2021 15:22:20 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 다리를 지나는 트럭 - 스택/큐</title>
      <link>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD-%EC%8A%A4%ED%83%9D%ED%81%90</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42583&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42583&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1626585776519&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;코딩테스트 연습 - 다리를 지나는 트럭&quot; data-og-description=&quot;트럭 여러 대가 강을 가로지르는 일차선 다리를 정해진 순으로 건너려 합니다. 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 알아내야 합니다. 다리에는 트럭이 최대 bridge_length대 올라갈&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42583&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42583&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jBnE5/hyKVbuZpcr/0LpL0sxkYpqaGoS2Cw2OGk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/x3NXw/hyKVgbZ0pU/qF19r3slP95kkmJct4riik/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42583&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42583&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jBnE5/hyKVbuZpcr/0LpL0sxkYpqaGoS2Cw2OGk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/x3NXw/hyKVgbZ0pU/qF19r3slP95kkmJct4riik/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - 다리를 지나는 트럭&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;트럭 여러 대가 강을 가로지르는 일차선 다리를 정해진 순으로 건너려 합니다. 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 알아내야 합니다. 다리에는 트럭이 최대 bridge_length대 올라갈&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제풀이&lt;/h3&gt;
&lt;pre id=&quot;code_1626585752511&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class Solution {
    public int solution(int bridge_length, int weight, int[] truck_weights) {
        List&amp;lt;Integer&amp;gt; done = new ArrayList&amp;lt;&amp;gt;();
        Queue&amp;lt;Integer&amp;gt; trucks = new LinkedList&amp;lt;Integer&amp;gt;();
        Queue&amp;lt;Integer&amp;gt; bridge = new LinkedList&amp;lt;Integer&amp;gt;();
        for (int truck : truck_weights) {
            trucks.add(truck);
        }

        // initialize queue (adding dummy data)
        for (int i = 0; i &amp;lt; bridge_length; i++) {
            bridge.add(0);
        }

        int answer = 0;
        int sum = 0;
        while (done.size() &amp;lt; truck_weights.length) {
            // 조건 만족
            if (trucks.peek() != null) {
                if (sum + trucks.peek() &amp;lt;= weight) {
                    int curr = trucks.poll();
                    bridge.add(curr);
                    sum += curr;
                }
            }
            if (bridge.size() &amp;lt; bridge_length) {
                bridge.add(0);
            }
            // iteration이 끝나면 빼주기
            int truck = bridge.poll();
            sum -= truck;
            if (truck != 0) {
                done.add(truck);
            }
            ++answer;
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <category>Queue</category>
      <category>Stack</category>
      <category>스택</category>
      <category>알고리즘</category>
      <category>알고리즘문제풀이</category>
      <category>큐</category>
      <category>프로그래머스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/103</guid>
      <comments>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD-%EC%8A%A4%ED%83%9D%ED%81%90#entry103comment</comments>
      <pubDate>Sun, 18 Jul 2021 14:23:17 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] K번째수 - 정렬</title>
      <link>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-K%EB%B2%88%EC%A7%B8%EC%88%98-%EC%A0%95%EB%A0%AC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6a737d;&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42748&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42748&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1626585526563&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;코딩테스트 연습 - K번째수&quot; data-og-description=&quot;[1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]] [5, 6, 3]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42748&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42748&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BGiNd/hyKVkk8431/6kGowy9g31HHf7Oc0LZAyK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ZhqpB/hyKVkk846F/jYOSVQUguf1nBn2afABeJ0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42748&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42748&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BGiNd/hyKVkk8431/6kGowy9g31HHf7Oc0LZAyK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ZhqpB/hyKVkk846F/jYOSVQUguf1nBn2afABeJ0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - K번째수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]] [5, 6, 3]&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제풀이&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1626585510990&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * K번째수
 * https://programmers.co.kr/learn/courses/30/lessons/42748
 */
public class Solution {

    public int[] solution(int[] array, int[][] commands) {
        List&amp;lt;int[]&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
        List&amp;lt;Integer&amp;gt; kth = new ArrayList&amp;lt;&amp;gt;();
        for (int[] command : commands) {
            list.add(Arrays.copyOfRange(array, command[0] -1 , command[1]));
            kth.add(command[2]);
        }

        int[] answer = new int[list.size()];
        for (int i = 0; i &amp;lt; list.size(); i++) {
            Arrays.sort(list.get(i));
            answer[i] = list.get(i)[kth.get(i) - 1];
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BE/algorithm</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <category>sorting</category>
      <category>알고리즘</category>
      <category>알고리즘문제풀이</category>
      <category>프로그래머스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/102</guid>
      <comments>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-K%EB%B2%88%EC%A7%B8%EC%88%98-%EC%A0%95%EB%A0%AC#entry102comment</comments>
      <pubDate>Sun, 18 Jul 2021 14:19:07 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 완주하지 못한 선수 - 해시</title>
      <link>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%ED%95%B4%EC%8B%9C</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42576&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 풀이&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1626585351312&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.HashMap;
import java.util.Map;

/**
 * 완주하지 못한 선수
 * https://programmers.co.kr/learn/courses/30/lessons/42576
 */
class Solution1 {

    public String solution(String[] participant, String[] completion) {
        Map&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();

        // convert array to hashmap for completion
        for (String person : participant) {
            if (map.containsKey(person)) {
                map.put(person, (int)map.get(person) + 1);
            } else {
                map.put(person, 1);
            }
        }

        // find participant who completed
        for(String person: completion) {
            map.put(person, map.get(person) - 1);
        }

        String answer = &quot;&quot;;
        // search for participant who did not complete
        for (String person: participant) {
            if (map.get(person) != 0) {
                answer = person;
                break;
            }
        }
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/algorithm</category>
      <category>java</category>
      <category>Programmers</category>
      <category>알고리즘</category>
      <category>알고리즘문제풀이</category>
      <category>프로그래머스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/101</guid>
      <comments>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%ED%95%B4%EC%8B%9C#entry101comment</comments>
      <pubDate>Sun, 18 Jul 2021 14:16:14 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 모의고사 - 완전탐색</title>
      <link>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC-%EC%99%84%EC%A0%84%ED%83%90%EC%83%89</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 링크&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42840&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42840&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1626585006210&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;코딩테스트 연습 - 모의고사&quot; data-og-description=&quot;수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다. 1번 수포자가 찍는 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42840&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42840&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eg94mn/hyKVfRF9WM/fJSzde7asYqhmvL1Iiyk90/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/wky3a/hyKU8yeALI/O43hPk4V7hxwNXkABebY4K/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42840&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42840&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eg94mn/hyKVfRF9WM/fJSzde7asYqhmvL1Iiyk90/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/wky3a/hyKU8yeALI/O43hPk4V7hxwNXkABebY4K/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - 모의고사&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다. 1번 수포자가 찍는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 풀이&lt;/h3&gt;
&lt;pre id=&quot;code_1626584940302&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.ArrayList;
import java.util.List;

/**
 * 프로그래머스:완전탐색:모의고사
 * https://programmers.co.kr/learn/courses/30/lessons/42840
 */
class Solution {
    public int[] solution(int[] answers) {
        int[] a = {1, 2, 3, 4, 5};
        int[] b = {2, 1, 2, 3, 2, 4, 2, 5};
        int[] c = {3, 3, 1, 1, 2, 2, 4, 4, 5, 5};
        int[] count = new int[3]; // 카운팅을 할 배열

        for (int i = 0; i &amp;lt; answers.length; i++) {
            if (a[i % a.length] == answers[i]) {
                count[0] = count[0] + 1;
            }
            if (b[i % b.length] == answers[i]) {
                count[1] = count[1] + 1;
            }
            if (c[i % c.length] == answers[i]) {
                count[2] = count[2] + 1;
            }
        }

        int max = 0;
        List&amp;lt;Integer&amp;gt; answerList = new ArrayList&amp;lt;&amp;gt;();
        // i+1 하는 이유는 학생의 넘버링이 zero-based 가 아니기 때문!
        for (int i = 0; i &amp;lt; count.length; i++) {
            if (max &amp;lt; count[i]) {
                answerList.clear();
                answerList.add(i + 1);
                max = count[i];
            } else if (max == count[i]) {
                answerList.add(i + 1);
            }
        }

        int[] answer = new int[answerList.size()];
        for(int i = 0; i &amp;lt; answer.length; i++) {
            answer[i] = answerList.get(i);
        }

        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BE/algorithm</category>
      <category>java</category>
      <category>Programmers</category>
      <category>알고리즘</category>
      <category>완전탐색</category>
      <category>프로그래머스</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/100</guid>
      <comments>https://yay-dev.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC-%EC%99%84%EC%A0%84%ED%83%90%EC%83%89#entry100comment</comments>
      <pubDate>Sun, 18 Jul 2021 14:09:30 +0900</pubDate>
    </item>
    <item>
      <title>[대충로그] grafana-loki query</title>
      <link>https://yay-dev.tistory.com/entry/%EB%8C%80%EC%B6%A9%EB%A1%9C%EA%B7%B8-grafana-loki-query</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;loki로 수집된 로그중에 json body (string)에서 값을 쿼리해야한다. loki로 수집된 로그중에 json body (string)에서 값을 쿼리해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1622687399210&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{container_name=&quot;도커컨테이너명&quot;} |= &quot; orderNo&quot; |= &quot;123123123&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;{ } 안의 값은 label&lt;/p&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>DevOps/monitoring</category>
      <category>Grafana</category>
      <category>Loki</category>
      <category>loki query</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/98</guid>
      <comments>https://yay-dev.tistory.com/entry/%EB%8C%80%EC%B6%A9%EB%A1%9C%EA%B7%B8-grafana-loki-query#entry98comment</comments>
      <pubDate>Thu, 3 Jun 2021 11:30:46 +0900</pubDate>
    </item>
    <item>
      <title>[ubuntu] python을 찾을 수 없다고 할때</title>
      <link>https://yay-dev.tistory.com/entry/ubuntu-python%EC%9D%84-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%97%86%EB%8B%A4%EA%B3%A0-%ED%95%A0%EB%95%8C</link>
      <description>&lt;pre id=&quot;code_1616030072552&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Command 'python' not found, but can be installed with:&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;라고 뜨는 경우가 있다. 하지만 python은 설치 되어있는데?  고민하지 말고 아래와 같이 입력해주자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1616030191059&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 터미널 (bashrc or bash_aliases)
vim ~/.bashrc

# bashrc (python3나 원하는 python 버전)
alias python=python3

# 터미널
source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 python을 입력하면, 원하는 버전에 맞게 잘 실행되는것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;스크린샷 2021-03-18 오전 10.17.30.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;120&quot; width=&quot;613&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9RA3E/btq0iXRAMAA/mclZWQvkKtqxKytAvKxEK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9RA3E/btq0iXRAMAA/mclZWQvkKtqxKytAvKxEK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9RA3E/btq0iXRAMAA/mclZWQvkKtqxKytAvKxEK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9RA3E%2Fbtq0iXRAMAA%2FmclZWQvkKtqxKytAvKxEK1%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-03-18 오전 10.17.30.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;120&quot; width=&quot;613&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/devops</category>
      <category>command python not found</category>
      <category>Python</category>
      <category>python3</category>
      <category>ubuntu</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/94</guid>
      <comments>https://yay-dev.tistory.com/entry/ubuntu-python%EC%9D%84-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%97%86%EB%8B%A4%EA%B3%A0-%ED%95%A0%EB%95%8C#entry94comment</comments>
      <pubDate>Thu, 18 Mar 2021 10:19:06 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] pip install 시 &amp;quot;Consider using the `--user` option or check the permissions&amp;quot; 에러</title>
      <link>https://yay-dev.tistory.com/entry/TIL-pip-install-%EC%8B%9C-Consider-using-the-user-option-or-check-the-permissions-%EC%97%90%EB%9F%AC</link>
      <description>&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;Consider using the `--user` option or check the permissions.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip로 패키지 설치시 이런 에러가 발생함.&lt;br /&gt;권한이 없는 디렉토리에서 설치하려고 하기 때문에 발생하는 에러.&lt;br /&gt;--user 옵션을 넣어서 설치하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;뭐든 모를땐 man page 나 -h를 사용해서 설명을 읽어본다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--user 옵션은 권한없는 디렉토리 (내경우의 C드라이브)가 아닌 사용자의 디렉토리 (home directory)에 설치해준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;  --user                      Install to the Python user install directory for your platform. Typically ~/.local/, or
                              %APPDATA%\Python on Windows. (See the Python documentation for site.USER_BASE for full
                              details.)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/etc</category>
      <category>pip</category>
      <category>pip install --user</category>
      <category>Python</category>
      <category>User</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/86</guid>
      <comments>https://yay-dev.tistory.com/entry/TIL-pip-install-%EC%8B%9C-Consider-using-the-user-option-or-check-the-permissions-%EC%97%90%EB%9F%AC#entry86comment</comments>
      <pubDate>Sat, 30 Jan 2021 21:58:26 +0900</pubDate>
    </item>
    <item>
      <title>crontab 을 사용해보자</title>
      <link>https://yay-dev.tistory.com/entry/crontab-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 주기적으로 작업을 해야하는 일을 생성할때는 종종 crontab을 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;cron으로 등록된 job list 확인&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;crontab -l&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;cron에 새로운 job을 등록&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;crontab -e &lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;cron 스케줄 expression&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적으로 cron을 실행시키기 위해서는 다음의 expression과 함께 등록해야한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[분] [시간] [날짜/월] [월] [날짜/주]&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;wildcard sign = any&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;code&gt;매주 0시 0분 (자정) 목요일에 해당 file path에 있는 스크립트를 실행&lt;/code&gt; 으로 해석하면 된다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;0 0 * * THU [file path]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cron schedule 유용한 링크: &lt;a href=&quot;https://crontab.guru/&quot;&gt;https://crontab.guru/&lt;/a&gt;&lt;/p&gt;</description>
      <category>DevOps/etc</category>
      <category>cron</category>
      <category>cron 스케줄</category>
      <category>crontab</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/85</guid>
      <comments>https://yay-dev.tistory.com/entry/crontab-%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90#entry85comment</comments>
      <pubDate>Wed, 4 Nov 2020 16:21:31 +0900</pubDate>
    </item>
    <item>
      <title>[curl] (60) SSL certificate problem: certificate has expired</title>
      <link>https://yay-dev.tistory.com/entry/curl-60-SSL-certificate-problem-certificate-has-expired</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;109&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w1HhC/btqKIoPYmh1/XUkrAe1Zb52TvEb7kgweH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w1HhC/btqKIoPYmh1/XUkrAe1Zb52TvEb7kgweH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w1HhC/btqKIoPYmh1/XUkrAe1Zb52TvEb7kgweH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw1HhC%2FbtqKIoPYmh1%2FXUkrAe1Zb52TvEb7kgweH0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;109&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;curl command 를 사용하다가 만난 이슈&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결방법 (임시방편)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl의 옵션인 -k (or --insecure)를 사용한다. certificate validation을 스킵해준다.&lt;/p&gt;</description>
      <category>DevOps/etc</category>
      <category>certificate has expired</category>
      <category>cURL</category>
      <category>curl 60</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/83</guid>
      <comments>https://yay-dev.tistory.com/entry/curl-60-SSL-certificate-problem-certificate-has-expired#entry83comment</comments>
      <pubDate>Mon, 12 Oct 2020 16:00:47 +0900</pubDate>
    </item>
    <item>
      <title>[vim] 좀 더 편-안하게 vim editor를 사용하는 설정</title>
      <link>https://yay-dev.tistory.com/entry/vim-%EC%A2%80-%EB%8D%94-%ED%8E%B8-%EC%95%88%ED%95%98%EA%B2%8C-vim-editor%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%84%A4%EC%A0%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;포매팅: shift, tab 4!!!!!!! 8이면 막 마음이 무너져 버려&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;set smartindent
set tabstop=4
set expandtab
set shiftwidth=4&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지금당장 라인끝으로 커서를 이동하고 싶다&lt;/h2&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ (shift + 4)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지금당장 파일의 제일 아래로 가고싶다&lt;/h2&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;shift + g&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지금당장 파일의 헤드로 가고싶다&lt;/h2&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;shift + h&lt;/code&gt;&lt;/pre&gt;</description>
      <category>devlog/TIL</category>
      <category>vim</category>
      <category>vim end of file</category>
      <category>vim end of line</category>
      <category>vim setting</category>
      <category>vim start of the file</category>
      <category>vim tab 4</category>
      <category>vim 설정</category>
      <category>vim 에디터 설정</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/81</guid>
      <comments>https://yay-dev.tistory.com/entry/vim-%EC%A2%80-%EB%8D%94-%ED%8E%B8-%EC%95%88%ED%95%98%EA%B2%8C-vim-editor%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%84%A4%EC%A0%95#entry81comment</comments>
      <pubDate>Wed, 7 Oct 2020 16:10:12 +0900</pubDate>
    </item>
    <item>
      <title>fluentd - host is unreachable</title>
      <link>https://yay-dev.tistory.com/entry/fluentd-host-is-unreachable</link>
      <description>&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2020-10-05 16:19:32 +0000 [warn]: #0 failed to flush the buffer. retry_time=15 next_retry_seconds=2020-10-05 21:09:56 +0000 chunk=&quot;5b0e6e3d58b7cec7f1d92eaa332995c8&quot; error_class=Fluent::Plugin::ElasticsearchOutput::RecoverableRequestFailure error=&quot;could not push logs to Elasticsearch cluster ({:host=&amp;gt;\&quot;호스트명\&quot;, :port=&amp;gt;포트번호, :scheme=&amp;gt;\&quot;http\&quot;}): Host is unreachable - connect(2) for IP주소 (Errno::EHOSTUNREACH)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그가 수집되지 않길래 fluentd의 로그를 확인해보니 이런 처음 보는 에러가 발생헀다.&lt;br /&gt;ES나 fluentd의 설정은 변경된게 없다. (추석연휴였는데 설정바뀔일이 없지!)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니, fluentd-ruby의 버그.&lt;br /&gt;fluentd -&amp;gt; ES 연결 시, host 설정이 proxy나 Load Balancing을 하게 되면 발생하는 에러라고 한다.&lt;br /&gt;fluentd-ruby host를 연결해주는 default class 말고 다른 class를 사용하면 해결되는 에러.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결방법&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default ruby class말고 다른 class 사용 (ElasticsearchSimpleSniffer)&lt;/li&gt;
&lt;li&gt;IP로 직접 연결&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 빠르게 수정할 수 있는 방법은 IP로 직접연결을 선택했다. 일단은 모니터링 해보고 다른 문제가 발생하거나 또 재현된다면 여기에 추가할 예정.&lt;/p&gt;</description>
      <category>DevOps/monitoring</category>
      <category>devops</category>
      <category>fluentd</category>
      <category>host is unreachable</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/80</guid>
      <comments>https://yay-dev.tistory.com/entry/fluentd-host-is-unreachable#entry80comment</comments>
      <pubDate>Tue, 6 Oct 2020 12:30:31 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] fluentd &amp;amp; NGINX</title>
      <link>https://yay-dev.tistory.com/entry/fluentd-%EC%9D%B4%EB%9E%98%EC%A0%80%EB%9E%98</link>
      <description>&lt;p&gt;다 설정하고 나니까 이런 에러가 로그에 남아있다&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;\[types removal\] Specifying types in bulk requests is deprecated.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것은 대체 무슨 로그?&lt;br /&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html#_why_are_mapping_types_being_removed&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html#_why_are_mapping_types_being_removed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;엘라스틱 서치 버전8부터는 mapping type이 없어진다고 한다. 아마 엘라스틱서치 어딘가에서 mapping type을 계속 쓰고있어서 그런것 같다. 아니면 내 fluentd 설정인가?&lt;br /&gt;이런 저런 이유라는데...어 읽어도 잘 모르겠다 지금은: &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.x/removal-of-types.html&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/7.x/removal-of-types.html&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;nginx access.log -&amp;gt; fluentd -&amp;gt; elasticsearch&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://hongsa.github.io/fluentd/&quot;&gt;http://hongsa.github.io/fluentd/&lt;/a&gt;&lt;br /&gt;여기 config 는 한글로 잘 되어있음. 참고&lt;/p&gt;
&lt;h2&gt;fluentd regex parser&lt;/h2&gt;
&lt;p&gt;검색하면 여기가 제일 먼저 뜨는데, 신뢰는 안간다. &lt;a href=&quot;https://fluentular.herokuapp.com/&quot;&gt;https://fluentular.herokuapp.com/&lt;/a&gt;&lt;br /&gt;나는 같은 ruby를 사용하는 ruby regex tester를 추천한다. &lt;a href=&quot;https://rubular.com/&quot;&gt;https://rubular.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;custom field를 추가했을 때 grafana에서 search 가 되지 않는 현상&lt;/h2&gt;
&lt;p&gt;Kibana에서 index를 생성하고 넘어올 때, 꼭 이 부분을 체크해야 된다.&lt;br /&gt;&lt;a href=&quot;https://discuss.elastic.co/t/in-kibana-i-have-fields-that-contains-a-question-mark-not-showing-in-metric-field/146017/2&quot;&gt;https://discuss.elastic.co/t/in-kibana-i-have-fields-that-contains-a-question-mark-not-showing-in-metric-field/146017/2&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;nginx request_time 의 의미가 헷갈렸다&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://forum.nginx.org/read.php?2,269720,269721#msg-269721&quot;&gt;https://forum.nginx.org/read.php?2,269720,269721#msg-269721&lt;/a&gt;&lt;br /&gt;그냥 request_time 의 값은 이미 seconds로 이해하면 된다.&lt;/p&gt;</description>
      <category>devlog/TIL</category>
      <category>fluentd</category>
      <category>fluentd regex parser</category>
      <category>nginx request_time</category>
      <category>til</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/79</guid>
      <comments>https://yay-dev.tistory.com/entry/fluentd-%EC%9D%B4%EB%9E%98%EC%A0%80%EB%9E%98#entry79comment</comments>
      <pubDate>Tue, 22 Sep 2020 18:36:58 +0900</pubDate>
    </item>
    <item>
      <title>뚝딱거리는 초보 devops의 TIL</title>
      <link>https://yay-dev.tistory.com/entry/%EB%9A%9D%EB%94%B1%EA%B1%B0%EB%A6%AC%EB%8A%94-%EC%B4%88%EB%B3%B4-devops%EC%9D%98-TIL</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;max_map_count가 뭔가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/11683850/how-much-memory-could-vm-use&quot;&gt;https://stackoverflow.com/questions/11683850/how-much-memory-could-vm-use&lt;/a&gt;&lt;br /&gt;이것이다. linux kernel&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sonarqube의 스펙은 잘 맞춰야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않으면 이런 에러가 나기때문이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;2020.08.28 02:28:49 ERROR es[][o.e.b.Bootstrap] node validation exception
[1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
ERROR: [1] bootstrap checks failed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker sonarqube image에는 이에 대한 설명히 아주 잘 나와있다.&lt;br /&gt;&lt;a href=&quot;https://hub.docker.com/_/sonarqube/&quot;&gt;https://hub.docker.com/_/sonarqube/&lt;/a&gt; -&amp;gt; Docker Host Requirements 이곳을 보시오&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ansible에서 이런 에러가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴때는 이렇게 설정을 바꿔라&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23074412/how-to-set-host-key-checking-false-in-ansible-inventory-file&quot;&gt;https://stackoverflow.com/questions/23074412/how-to-set-host-key-checking-false-in-ansible-inventory-file&lt;/a&gt;&lt;/p&gt;</description>
      <category>devlog/TIL</category>
      <category>devops</category>
      <category>til</category>
      <author>bandal-gom</author>
      <guid isPermaLink="true">https://yay-dev.tistory.com/78</guid>
      <comments>https://yay-dev.tistory.com/entry/%EB%9A%9D%EB%94%B1%EA%B1%B0%EB%A6%AC%EB%8A%94-%EC%B4%88%EB%B3%B4-devops%EC%9D%98-TIL#entry78comment</comments>
      <pubDate>Thu, 3 Sep 2020 09:27:55 +0900</pubDate>
    </item>
  </channel>
</rss>