<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>파이문</title>
    <link>https://pymoon.tistory.com/</link>
    <description>데이터 깎는 노인</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 03:06:39 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>민Z</managingEditor>
    <image>
      <title>파이문</title>
      <url>https://tistory1.daumcdn.net/tistory/2003313/attach/1da52d5e46a547c9b435108dfc55ff42</url>
      <link>https://pymoon.tistory.com</link>
    </image>
    <item>
      <title>기쁨과 즐거움은 무슨 차이일까</title>
      <link>https://pymoon.tistory.com/entry/%EA%B8%B0%EC%81%A8%EA%B3%BC-%EC%A6%90%EA%B1%B0%EC%9B%80%EC%9D%80-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EC%9D%BC%EA%B9%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;희노애락중에 희와 락은 무슨 차이일까.&amp;nbsp;&lt;/p&gt;</description>
      <category>메모장</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/203</guid>
      <comments>https://pymoon.tistory.com/entry/%EA%B8%B0%EC%81%A8%EA%B3%BC-%EC%A6%90%EA%B1%B0%EC%9B%80%EC%9D%80-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EC%9D%BC%EA%B9%8C#entry203comment</comments>
      <pubDate>Sun, 18 Aug 2024 12:10:51 +0900</pubDate>
    </item>
    <item>
      <title>[Error] has been compiled by a more recent version of the Java Runtime</title>
      <link>https://pymoon.tistory.com/entry/Error-has-been-compiled-by-a-more-recent-version-of-the-Java-Runtime</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot에서 multiple module로 예제들을 정리하던 중 오래된 모듈 실행 시 다음 에러 발생&lt;/p&gt;
&lt;pre id=&quot;code_1723950133860&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Error: A JNI error has occurred, please check your installation and try again
Exception in thread &quot;main&quot; java.lang.UnsupportedClassVersionError: com/example/javaspringevent/JavaSpringEventApplication has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:601)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Edit Configuration에서 자바 버젼을 확인 후 프로젝트에 맞는 자바 버젼으로 바꿔주자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-18 오후 12.03.26.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ME3L5/btsI7W7eth9/JPtlqtwqBQLQDKiteQ7BP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ME3L5/btsI7W7eth9/JPtlqtwqBQLQDKiteQ7BP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ME3L5/btsI7W7eth9/JPtlqtwqBQLQDKiteQ7BP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FME3L5%2FbtsI7W7eth9%2FJPtlqtwqBQLQDKiteQ7BP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1618&quot; height=&quot;1372&quot; data-filename=&quot;스크린샷 2024-08-18 오후 12.03.26.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java/Spring-boot</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/202</guid>
      <comments>https://pymoon.tistory.com/entry/Error-has-been-compiled-by-a-more-recent-version-of-the-Java-Runtime#entry202comment</comments>
      <pubDate>Sun, 18 Aug 2024 12:04:27 +0900</pubDate>
    </item>
    <item>
      <title>맥에서 mx master 마우스 휠 정상 동작 하지 않을 때</title>
      <link>https://pymoon.tistory.com/entry/%EB%A7%A5%EC%97%90%EC%84%9C-mx-master-%EB%A7%88%EC%9A%B0%EC%8A%A4-%ED%9C%A0-%EC%A0%95%EC%83%81-%EB%8F%99%EC%9E%91-%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;휠 설정이 갑자기 (진짜 갑자기!) 반대로 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맥에서 마우스 설정도 변경해보고 logi options 설정도 변경해보았지만 휠을 위로 올리면 스크롤이 위로 올라갔다. (일반 윈도우 휠 방식 처럼 동작함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에 찾아보니 mx master 마우스 휠 관련 이슈들이 꽤 있었고 대부분은 휠 자체가 동작하지 않는다는 이슈였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 결론부터 말하자면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-06 오후 9.40.39.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ow7mV/btrNYpxNz6Z/mCfmyjF6GJjYkjB41psMd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ow7mV/btrNYpxNz6Z/mCfmyjF6GJjYkjB41psMd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ow7mV/btrNYpxNz6Z/mCfmyjF6GJjYkjB41psMd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOw7mV%2FbtrNYpxNz6Z%2FmCfmyjF6GJjYkjB41psMd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;752&quot; data-filename=&quot;스크린샷 2022-10-06 오후 9.40.39.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #781b33; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;활성 상태 보기 (Activity Monitor) 에서 logi Options Daemon 을 강제 종료하면 정상 동작한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로지텍의 logi options 와 새로 나온 logi options+ 를 같이 사용하면서 생긴 버그가 아닌가 추측 됨&lt;/p&gt;</description>
      <category>기타</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/200</guid>
      <comments>https://pymoon.tistory.com/entry/%EB%A7%A5%EC%97%90%EC%84%9C-mx-master-%EB%A7%88%EC%9A%B0%EC%8A%A4-%ED%9C%A0-%EC%A0%95%EC%83%81-%EB%8F%99%EC%9E%91-%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C#entry200comment</comments>
      <pubDate>Thu, 6 Oct 2022 21:41:23 +0900</pubDate>
    </item>
    <item>
      <title>로지텍 mx keys mini 후기 (부제. 나의 키보드 후기들)</title>
      <link>https://pymoon.tistory.com/entry/%EB%A1%9C%EC%A7%80%ED%85%8D-mx-keys-mini-%ED%9B%84%EA%B8%B0</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면, 로지텍 mx keys mini 를 현재 메인으로 쓰고 있다. 그만큼 만족도가 높았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무접점을 쓴 이후로 펜타그래프를 쓸 일은 없을 거라고 생각했는데, mx keys mini는 사람들이 극찬한 이유가 있었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레오폴드 750R 청축&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임할 때 주로 사용하였다. 청축 이라는 이름 답게 매우 시끄러웠으나 키보드 치는 맛은 쏠쏠했다. 무접점을 사용한 이후로는 청축 키보드는 창고행이 되었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레오폴드 FC660C&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업을 하면서 업무용 키보드에 대해 관심이 가기 시작했다. 찾아보니 무접점이라는 키보드가 있다는 걸 알게 되었고, 타건감이 초콜릿을 도각도각 하는 느낌이라고 했다. 글만 봤을 때는 이해하기 어려웠으나 직접 타건하러 가기는 귀찮았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알지 못하는 타건감에 큰 돈 들이긴 싫어서 (당시 해피해킹, 리얼포스 등이 있었지만 30만원을 훌쩍 넘어 부담스러우기도 했고) 상대적으로 저렴한 레오폴드 FC660C를 구매하였다.&lt;br /&gt;&lt;br /&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;어쨌든 당시에는 꽤 마음에 들어서 회사에서 주던 키보드는 반납하고 레오폴드 FC660C를 업무용으로 사용하였었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 키보드가 얼마나 마음에 들었냐면, 한 대 더 사서 동생한테 선물할 정도였다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;FC660C를 잘 썼지만 마음 한 켠으로는 해피해킹이나 리얼포스가 궁금했다. 사내에서 사용하시던 분이 계셔서 타건을 해보았는데 솔직히 FC660C보다 더 나은 점은 딱히 못 느꼈다. 그런데... 그냥 갖고 싶었다. (???)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 (FC660C가 65 배열이여서 리얼포스보다는) 해피해킹을 사야겠다고 마음 먹었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해피해킹 타입S&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 중고 거래 잘 안하는 타입인데 어쩌다가 중고거래로 구매하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후기를 많이 읽어보았다. 대부분 키 적응에 실패한 사람들의 중고거래 후기가 많았다. 하지만 구매하겠다는 마음에 영향을 줄 순 없었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;생각해보면 평소에 기존 방향키들을 잘 쓰지 않았기 때문에, 새로운 방향키 조합이 사용하기에 어렵지 않았던 것 같다. (일 할 때는 에디터에 vim 모드를 썼고, 요새 게임들은 WASD 조합이라 사용할 일이 없다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드만 쓰면 심심하니까 주변 악세서리도 구매하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팜레스트의 경우 원래 FC660C 쓸 때 사용하던 것이 너무 마음에 들어서 다른 버젼으로 하나 더 샀다. (구매처는 &lt;a href=&quot;https://smartstore.naver.com/joseph_/?NaPm=ct%3Dl4l32xl6%7Cci%3Dcheckout%7Ctr%3Dds%7Ctrx%3D%7Chk%3D531d9037ad7496a2f0b4f49a1c4e5d933d8f89f9&quot; target=&quot;_self&quot;&gt;&lt;span&gt;죠셉 공방 스토어)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드 관심있는 사람은 알겠지만, 키보드 튜닝의 끝판왕은 키캡이다. (사실 스위치가 본체고 그 다음이 윤활이지만 무접점은 해당 안되니까  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 무접점은 공급이 없어도 너무 없었다. 그나마 리얼포스 정도가 유명한데 원하는 색도, 스타일도 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 어찌저찌 얼마 없는 공급 속에서 잘 찾아서 구매를 하였는데 문제가 생겼다. 키캡마다 촉감과 높이가 생각 이상으로 달랐던 것이다. 근데 환불하기도 귀찮고 중고거래도 안하는 스타일이라 참으면서 사용했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iu0MX/btrFci7Tmsn/ZxtsQy2IlhCK2LecA0Ju91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iu0MX/btrFci7Tmsn/ZxtsQy2IlhCK2LecA0Ju91/img.png&quot; data-alt=&quot;깔끔한게 좋아서 무각을 샀는데, 숫자키만은 도저히 외워서는 못 치겠더라. 그래서 위 사진처럼 포인트를 줬다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iu0MX/btrFci7Tmsn/ZxtsQy2IlhCK2LecA0Ju91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIu0MX%2FbtrFci7Tmsn%2FZxtsQy2IlhCK2LecA0Ju91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1866&quot; height=&quot;1226&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1226&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안타깝게도 이렇게 악세서리도 열심히 구매할 정도로 애정을 가졌던 해피해킹에는 2가지 단점이 있었는데&lt;br /&gt;&lt;br /&gt;첫째는, 블루투스 모델이 아니였다는 것이다.&lt;br /&gt;두번째는 키 스트로크가 깊은게 은근이 신경 쓰였다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음에 안들기 시작하면, 사실 이유야 갖다 붙이면 그만이었다. 그렇게 진짜 몇 날 며칠째 고민하다가 mx keys mini를 샀다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로지텍 MX keys mini&lt;/h4&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;그렇게 고민을 많이 하면서 기계식을 사려했는데 왜 mx keys를 샀을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tv6QE/btrE76APmaZ/AX81vMHCKYMjh6IjfygwXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tv6QE/btrE76APmaZ/AX81vMHCKYMjh6IjfygwXK/img.png&quot; data-alt=&quot;출처는 로지텍 사이트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tv6QE/btrE76APmaZ/AX81vMHCKYMjh6IjfygwXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTv6QE%2FbtrE76APmaZ%2FAX81vMHCKYMjh6IjfygwXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;384&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;384&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;로지텍 키보드를 산 이유는 5가지다.&lt;br /&gt;&lt;br /&gt;1. 로지텍 mx master 마우스를 쓰고 있어서 세트로 사용하고 싶었다.&lt;br /&gt;2. 키보드 색깔이 그레이였다. 개인적으로 챠콜, 그레이, 블랙 짱 좋아한다.&lt;br /&gt;3. 블루투스다.&lt;br /&gt;4. 펜타그래프... 오랜만에 써보고 싶었다.&lt;br /&gt;5. 사람들이 다들 좋다했다. (제일 중요! 귀가 얇다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기에 보태보태 병으로 비싼 키보드만 계속 보다보니 상대적으로 저렴하게 느껴져서 바로 주문했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;당시 최애 유튜버 바로 드보키였는데 키보드 가격이 정말..&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;사용을 해보니 아래와 같은 장점이 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기계식이나 무접점이랑 달리 스트로크가 짧아서 키보드 치는데 힘이 많이 안들어, 오타율이 적어졌다. 키압은 다른 키보드에 비해 높다고 하는데 체감은 잘 안된다.&lt;/li&gt;
&lt;li&gt;블루투스여서 선이 없다. 덕분에 책상이 깔끔해졌으며 백라이트가 생각보다 꽤 이쁘다. (백라이트 켜놓으면 배터리가 금세 소모되어, 충전이 좀 귀찮긴 함)&lt;/li&gt;
&lt;li&gt;키보드가 낮아서 팜레스트가 (살) 필요 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 살 때 공짜로 주는 키보드들 때문에 펜타그래프에 대한 편견이 있었는데 그 편견이 다 없어졌다. 그리고 나름 타건하는 재미도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;단점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 방향키! 해피해킹에 익숙해진 펑션 키 조합에서 정말 &lt;b&gt;불호 불호 불호&lt;/b&gt; 하는 &lt;b&gt;우측 하단의 조그마한 방향키&lt;/b&gt;를 다시 보게 될 줄이야... 저 위치, 저 사이즈 도대체 뭘까..? 구색맞추기 용으로 억지로 넣은 느낌이다.&lt;/li&gt;
&lt;li&gt;키캡 놀이가 불가능하다. (ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ)&lt;/li&gt;
&lt;li&gt;무게가 엄청 가볍지만은 않다. 체감상 애플 무선 키보드보다 좀 무거운 듯 싶다. (하지만 애플 키보드는 스페이스 그레이가 없으니 로지텍 압승)&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로지텍 키 재 매핑&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해피해킹이나 FC660C 는 4개의 물리 스위치를 통해 키를 다르게 쓰는 방식을 제공하고 있다. 나는 캡스락을 컨트롤로 쓰고 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;제발 캡스락 컨트롤로 안 써본 사람 없게 해주세요. 진짜 짱 편함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 로지텍 mx keys mini 는 물리 스위치도 없고 logi option에서 키 매핑을 제공해주지도 않았다. 요즘 키보드들은 via 같은 자체 config 시스템을 제공해주었기에 살짝 놀랐다.&lt;br /&gt;&lt;br /&gt;다행히도 애플은 설정에서 손쉽게 키 조합을 변경할 수 있었다.&lt;br /&gt;&lt;br /&gt;맥북에서 키보드 설정 들어간 후에 우측 하단의 보조 키 버튼을 눌러서 변경하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MxNam/btrE9Rb7ikU/LFQzB9iBA6Lesw8rEu1TH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MxNam/btrE9Rb7ikU/LFQzB9iBA6Lesw8rEu1TH0/img.png&quot; data-alt=&quot;우측 하단의 보조 키 버튼 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MxNam/btrE9Rb7ikU/LFQzB9iBA6Lesw8rEu1TH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMxNam%2FbtrE9Rb7ikU%2FLFQzB9iBA6Lesw8rEu1TH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;708&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;708&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;변경 하고 싶은 키를 매핑한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyZR4p/btrE6yR9AvH/SvEA7ZidTMwvg5vjJe8kZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyZR4p/btrE6yR9AvH/SvEA7ZidTMwvg5vjJe8kZ1/img.png&quot; data-alt=&quot;여기서 설정 변경 하면 됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyZR4p/btrE6yR9AvH/SvEA7ZidTMwvg5vjJe8kZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyZR4p%2FbtrE6yR9AvH%2FSvEA7ZidTMwvg5vjJe8kZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;708&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;708&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;그러면 Caps lock 키를 Control 키로 잘 사용할 수 있다.&lt;br /&gt;&lt;br /&gt;윈도우의 경우도 변경을 했는데 윈도우는 &lt;a href=&quot;https://coldmater.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;https://coldmater.tistory.com/193&lt;/span&gt;&lt;/a&gt; 이 블로그를 보고 따라 했는데 잘 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 mx keys mini에 완전 정착했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2024.02.11 업데이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불행히도 mx keys mini에 만족하지 못하고 중간에 키보드를 몇 대 더 들였다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;블루투스 기계식 키보드를 사고 싶어서 레오폴드 660mbt 실버축&lt;/li&gt;
&lt;li&gt;실버축 너무 심심해서 엠스톤 저소음 갈축&lt;/li&gt;
&lt;li&gt;75 배열이 마음에 안들어서 작은 키보드 알아보다가... 결국 커스텀 키보드 qk65 v2 구매&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해서 지금은 qk65 v2를 사용하고 있다. 윤활은 아직 나에겐 너무 어려운 작업이라 일단 원하는 키캡과 스위치를 사용해볼 수 있다는 데에 의의를 두고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mx keys mini는 세컨드로 사용중인데,&amp;nbsp;결국엔 커스텀 키보드로 오게될 것을 참 멀게도 돌아왔다.  &lt;/p&gt;</description>
      <category>기타</category>
      <category>후기</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/194</guid>
      <comments>https://pymoon.tistory.com/entry/%EB%A1%9C%EC%A7%80%ED%85%8D-mx-keys-mini-%ED%9B%84%EA%B8%B0#entry194comment</comments>
      <pubDate>Sun, 19 Jun 2022 18:18:05 +0900</pubDate>
    </item>
    <item>
      <title>github action 알아보기 (docker 예시 포함)</title>
      <link>https://pymoon.tistory.com/entry/github-action-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;github action 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action 은 github 가 제공하는 CI/CD 플랫폼이라고 볼 수 있다. 보통 현업에서 빌드/배포를 진행할 때 별도의 서버를 두고 작업을 하곤 한다. (이 때 두는 서버는 물리 서버일 수도 있고 가상 서버일 수도 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action 이 다른 CI/CD (예를 들면 젠킨스) 와 다른 강력한 장점은 바로 트리거 기능이라고 생각한다. (어디까지나 주관적인 의견입니다.) github 에 hook 을 걸어, push event 를 다른 툴에서 체킹을 하고 빌드가 자동으로 이루어지는데 github action 을 사용하면 yml 로 간편하게 이러한 작업을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 여러가지 action 들을 사용하여 CI/CD 작업 (이를 Workflow 라고 한다.) 시 필요한 기능(빌드 시 특정 인자 값을 넣는다던지, 작업 성공 시 메시지를 보낸다던지 하는 등의 서드파티 작업)들을 손쉽게 추가할 수 있다는 장점도 있다.&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;단점은 github 를 사용해야만 쓸 수 있다는 점이 있고, 회사에서 github enterprise server 를 사용한다고 하더라도 devops 에서 추가해주지 않으면 사용할 수 없다.&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;github action 찍먹하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action 은, 정확히는 runner 라는 머신 위에서 여러 action 작업들이 하나의 workflow 형식으로 진행된다. 지금은 여기까지만 보고 일단 실습을 통해 github action 을 내 저장소에 설정해보도록 하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;github action workflow yml 추가하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github 저장소의 상단에 Actions 를 눌러 손쉽게 workflow yml 을 추가할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.27.03.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PnbvQ/btrE5R3ezZU/R2gxjBJA2o7bC10Mvxa6p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PnbvQ/btrE5R3ezZU/R2gxjBJA2o7bC10Mvxa6p0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PnbvQ/btrE5R3ezZU/R2gxjBJA2o7bC10Mvxa6p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPnbvQ%2FbtrE5R3ezZU%2FR2gxjBJA2o7bC10Mvxa6p0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;945&quot; height=&quot;92&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.27.03.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.28.20.png&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqYDiQ/btrE5s3G9KW/bfjk18UNDzzGVBCDUEP8N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqYDiQ/btrE5s3G9KW/bfjk18UNDzzGVBCDUEP8N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqYDiQ/btrE5s3G9KW/bfjk18UNDzzGVBCDUEP8N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqYDiQ%2FbtrE5s3G9KW%2Fbfjk18UNDzzGVBCDUEP8N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;373&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.28.20.png&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Simple workflow 를 클릭하면 가장 기본 설정만 들어있는 yml 템플릿이 나타나게 된다. 일단 아래 yml 변경 없이 commit 해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.27.38.png&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6TU53/btrE4WRMrrT/kOPX6qNflz7e6Qt3MZ4eb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6TU53/btrE4WRMrrT/kOPX6qNflz7e6Qt3MZ4eb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6TU53/btrE4WRMrrT/kOPX6qNflz7e6Qt3MZ4eb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6TU53%2FbtrE4WRMrrT%2FkOPX6qNflz7e6Qt3MZ4eb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;596&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.27.38.png&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yml 만 추가한다고 바로 해당 repository 의 github action workflow 가 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action 은 runner 라고 불리는 머신이 필요하다. yml 에서는 runs-on: ubuntu-latest 라는 설정이 있는데 이는 ubuntu-latest 라는 이름을 가진 runner 에서 해당 workflow 를 실행하겠다는 의미이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;github action runner 설정하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runner 는 가상 머신이라고 보면 된다. 가상 머신이라고 해서 무언가를 설치하거나 설정해야할 것 같지만 github 는 self-hosted runner 라는 개념으로 로컬에서도 runner 를 만들 수 있다. (이름은 따로 정의할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 github 저장소로 가서 Settings 를 클릭한 후 좌측의 Actions -&amp;gt; Runners 를 눌러본다. 그러면 아래 처럼 New self-hosted runner 버튼이 나오게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.33.02.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epsVCi/btrE5h2py4q/xlkykKK55sKijHKaRMWAw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epsVCi/btrE5h2py4q/xlkykKK55sKijHKaRMWAw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epsVCi/btrE5h2py4q/xlkykKK55sKijHKaRMWAw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepsVCi%2FbtrE5h2py4q%2FxlkykKK55sKijHKaRMWAw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;551&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.33.02.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 해당 버튼을 클릭하면 OS에 맞게 설정해야하는 내용을 github 에서 가이드해주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.33.12.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xQTfw/btrE4Czcpux/4Nl1C1WAyKNGkGLjrwdVH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xQTfw/btrE4Czcpux/4Nl1C1WAyKNGkGLjrwdVH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xQTfw/btrE4Czcpux/4Nl1C1WAyKNGkGLjrwdVH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxQTfw%2FbtrE4Czcpux%2F4Nl1C1WAyKNGkGLjrwdVH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;878&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.33.12.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 내 로컬은 MacOS 이기 때문에 Mac 설정을 진행하였다. (여기선 로컬이지만, 당연하게도 개인 서버가 있다면 거기서도 진행해도 된다. 현업에서는 주로 도커로 구성된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config.sh 를 실행하면 아래와 같이 설정하는 화면이 나올 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.36.06.png&quot; data-origin-width=&quot;1627&quot; data-origin-height=&quot;914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deUxiD/btrE3T9dcHK/JRFhLE3CZknOt69ePNlwSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deUxiD/btrE3T9dcHK/JRFhLE3CZknOt69ePNlwSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deUxiD/btrE3T9dcHK/JRFhLE3CZknOt69ePNlwSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeUxiD%2FbtrE3T9dcHK%2FJRFhLE3CZknOt69ePNlwSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1627&quot; height=&quot;914&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.36.06.png&quot; data-origin-width=&quot;1627&quot; data-origin-height=&quot;914&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 값도 넣지 않고 엔터키만 쳤기 때문에 가장 기본 값으로 설정이 된다. configure 는 나중에도 변경할 수 있기 때문에 부담없이 진행해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 중 This runner will have the following labels: 'self-hosted', 'macOS', 'X64' 이라는 걸 볼 수 있는데 이는 내 runner 의 label 을 붙여주는 작업이다. 여기서는 가장 기본 값이 붙었는데, 이 값을 어디서 사용하냐면 runs-on: self-hosted 으로 workflow yml 파일에서 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 workflow 에 작성되어있는 job 을 어느 runner 에서 실행 시킬 것인지 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;다시 github 저장소로 가보면 동일한 label 이름들로 runner 가 설정되어있는 걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.38.52.png&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8nkwB/btrE5iG11gb/1a30on2mOkcgrejLpqE3Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8nkwB/btrE5iG11gb/1a30on2mOkcgrejLpqE3Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8nkwB/btrE5iG11gb/1a30on2mOkcgrejLpqE3Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8nkwB%2FbtrE5iG11gb%2F1a30on2mOkcgrejLpqE3Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1157&quot; height=&quot;478&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.38.52.png&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runner 상태는 총 3가지로 Offline, Active, Idle 상태를 가지며 현재는 runner 를 실행시키지 않았기 때문에 (run.sh 를 실행하지 않았기 때문에) Offline 으로 보이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runner 가 띄워져있지만 작업을 하지 않으면 Idle 로, Job 이 실행되고 있으면 Active 로 보여지게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;github action workflow yml 수정하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본으로 들어가 있던 yml 은 (여기서는 blank.yml 이라는 이름을 가지고 있다.) runs-on 이 ubuntu-latest 이기 때문에 이 값을 self-hosted 로 변경하도록 한다. (github 에서 직접 파일을 수정해도 된다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.46.15.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRdYD0/btrE4U7uzLa/cdbcDXijgqVXr35qiwvv3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRdYD0/btrE4U7uzLa/cdbcDXijgqVXr35qiwvv3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRdYD0/btrE4U7uzLa/cdbcDXijgqVXr35qiwvv3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRdYD0%2FbtrE4U7uzLa%2FcdbcDXijgqVXr35qiwvv3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1293&quot; height=&quot;503&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.46.15.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 작업하고 있는 브랜치가 master 라면, 파일 변경 후 저장 (commit) 하면 바로 github actions runner 로 트리거 되서 runner 가 돌아가게 될 것이다. master 브랜치가 아니라면 trigger 룰을 수정해야 한다. 위의 예제와 동일하다면 push.branches 값을 추가하거나 수정하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.44.53.png&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rceJa/btrE5rKwoTC/COJbn1qoRmYRhOi2yUMO00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rceJa/btrE5rKwoTC/COJbn1qoRmYRhOi2yUMO00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rceJa/btrE5rKwoTC/COJbn1qoRmYRhOi2yUMO00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrceJa%2FbtrE5rKwoTC%2FCOJbn1qoRmYRhOi2yUMO00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;824&quot; height=&quot;120&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.44.53.png&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정과 즉시 runner 가 Active 상태가 되고 작업이 완료 되면 Actions 탭에서 이를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.47.19.png&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz7IQC/btrE5RIYzu8/oKICGVJMRxaiceHs9ML4N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz7IQC/btrE5RIYzu8/oKICGVJMRxaiceHs9ML4N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz7IQC/btrE5RIYzu8/oKICGVJMRxaiceHs9ML4N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz7IQC%2FbtrE5RIYzu8%2FoKICGVJMRxaiceHs9ML4N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;477&quot; data-filename=&quot;스크린샷 2022-06-17 오후 8.47.19.png&quot; data-origin-width=&quot;1172&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;여기까지 했으면 runner 는 이제 다 아는 거나 다름 없다. 개인 프로젝트로 할 경우 이 정도면 충분하고, 엔터프라이즈라면 아마 Devops 팀에서 runner 를 관리해줄 것이다. (이 때는 보통 도커/쿠버네티스를 많이 사용하며, 하나의 작업이 끝나면 runner 는 clean up 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 build 아래의 각 내용 (Set up job, Run actions/checkout@v3... 등등) 이  github action 의 step 이라는 개념이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;github action 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runner 설정도 끝났고 심플한 yml 도 추가하였으니 이제 실제로 빌드 하는 건 어떻게 하는지 살펴보도록 한다. 빌드는 자바, gradle, docker image 기준으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신에서 자바를 gradle로 빌드할 때 아래와 같은 명령어를 주로 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1655466681404&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./gradlew build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 runner 에 그대로 넣으면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;github action  job&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action 은 job 을 기준으로 실행되고 각 job 은 step 여러개로 이루어진다. (step 은 하나일 수도 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;job 은 순서가 구분 없이 병렬로 실행되지만, step 은 명시되어 있는 순서대로 실행된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;github action gradle build 예시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 java 17 로 gradle build 를 하는 예시이다.&lt;/p&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;actions/checkout 은 무조건 처음에 명시&lt;/b&gt;해야 한다. 이 액션은 git pull 과 같은 의미로 프로젝트를 가져오겠다는 뜻을 갖고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버젼의 경우 적당히, 환경에 맞게, 하지만 가능하면 최신버젼으로 사용하면 된다. (action 이름이 actions/checkout 이기 때문에 동일한 이름의 저장소가 있으므로 직접 가서 어떻게 액션을 만들었는지, 최신 버젼은 몇인지 볼 수 있다. &lt;a href=&quot;https://github.com/actions/checkout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/actions/checkout&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1655466988828&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: CI test

on:
  push:
    branches:
      - 'master'

  workflow_dispatch:


jobs:
  build:
    runs-on: [ self-hosted ]
    steps:
      - uses: actions/checkout@v2
      - name: Set up java 17
        uses: actions/setup-java@v2
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Build project with gradle
      	# run 할 명령어를 입력한다.
        run: ./gradlew -x test build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jobs 뒤에 붙는 build 는 예약어가 아니기 때문에 원하는 값을 사용해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명했지만 jobs 는 순서 보장이 안되기 때문에 아래 처럼 작성할 경우 job이 병렬적으로 실행된다. 아래 예시로는 build 라는 job 과 docker 라는 job 이 동시에 실행될 것이다. (동시는 아니더라도 순서 보장이 안됨)&lt;/p&gt;
&lt;pre id=&quot;code_1655467236843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;jobs:
  build:
    runs-on: [ self-hosted ]
    steps:
      - uses: actions/checkout@v3

      - name: Run a one-line script
        run: echo Hello, world!

      - name: Run a multi-line script
        run: |
          echo Add other actions to build,
          echo test, and deploy your project.
  docker:
    runs-on: [ self-hosted ]
    steps:
      - name: Login a docker
        run: echo Hello, docker!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 job 끼리 순서를 명시하고 싶으면 needs 옵션을 사용해야 한다.&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-06-17 오후 9.03.19.png&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGGwP7/btrE3HgW8EY/UgP6R8i2XNcXcE3oYMbqI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGGwP7/btrE3HgW8EY/UgP6R8i2XNcXcE3oYMbqI0/img.png&quot; data-alt=&quot;순서 보장 없이 실행 됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGGwP7/btrE3HgW8EY/UgP6R8i2XNcXcE3oYMbqI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGGwP7%2FbtrE3HgW8EY%2FUgP6R8i2XNcXcE3oYMbqI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;347&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.03.19.png&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;347&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;needs 옵션을 사용하여서 build 후 docker job 이 실행되도록 설정해보자&lt;/p&gt;
&lt;pre id=&quot;code_1655467551701&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  docker:
    runs-on: [ self-hosted ]
    # 아래 처럼 job 이름을 명시한다. 리스트 형식도 가능하다.
    needs: build
    steps:
      - name: Login a docker
        run: echo Hello, docker!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.05.20.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/taiuI/btrE4nPZGzm/jnb1ZNZh1YTxWEH3APnSn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/taiuI/btrE4nPZGzm/jnb1ZNZh1YTxWEH3APnSn0/img.png&quot; data-alt=&quot;needs 를 사용하면 build job 이 끝나기 전까지 docker job 이 실행되지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/taiuI/btrE4nPZGzm/jnb1ZNZh1YTxWEH3APnSn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtaiuI%2FbtrE4nPZGzm%2Fjnb1ZNZh1YTxWEH3APnSn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;275&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.05.20.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;needs 를 사용하면 build job 이 끝나기 전까지 docker job 이 실행되지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;job 간에 의존성을 갖는 경우엔 이 처럼 사용하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;github action 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 여기까지만 보면 이제 github action 은 다 안다고 보면 된다. 나머지는 본인한테 필요한 설정들을 한다던가, 유용한 action 들을 찾아서 프로젝트에 맞게 사용한다던가 등등을 하는 과정이다.&lt;/p&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 라는 job 이 있지만 아무것도 하지 않는데, Docker 가 제공하는 action 들 (build, push, meta 등) 을 사용하여 내가 만든 jar 를 Docker image 로 다시 빌드한다던지 하는 등의 작업을 진행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 Action 들은 직접 만들수도 있고 이미 있는 걸 가져다가 쓸 수도 있는데 github 는 Marketplace 라는 곳에서 사람들이 올린 action 들을 볼 수 있다. (오픈 소스를 가져다가 쓴다고 보면 되고 실제로도 오픈 소스이다.)&lt;/p&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/marketplace?type=actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/marketplace?type=actions&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;github action 으로 도커 빌드하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 를 사용할 때 필요한 docker registry 로그인, docker build, docker push 등의 작업을 Docker github 가 제공하는 Action 들로 진행해보도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(굳이 아래 내용을 따라야할 필요는 없고, 본인의 action 을 쓴다던가 다른 사람이 만든 action 을 써도 된다.)&lt;/p&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/search?q=topic%3Agithub-actions+org%3Adocker+fork%3Atrue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/search?q=topic%3Agithub-actions+org%3Adocker+fork%3Atrue&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker registry 로그인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;env 의 경우엔 정의한 변수들을 사용하는 부분이고(파일에 추가해도 상관 없는 내용), secrets 같은 경우엔 보안 상 보여지면 안되는 비밀 값으로 github 에서 설정해야 한다. 키는 DOCKER_USER, DOCKER_PASSWORD 가 되고 값은 직접 입력하면 되는데&lt;/p&gt;
&lt;pre id=&quot;code_1655468091489&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      - name: Login to d2hub
        uses: docker/login-action@v1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_PASSWORD }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github 에서 아래 화면에서 입력하면 된다. 우측 상단의 New repository secret 눌러서 설정하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.16.02.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2D6R5/btrE4Bf2vbl/SZUVVmsXjyKDA6E9S7Oe30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2D6R5/btrE4Bf2vbl/SZUVVmsXjyKDA6E9S7Oe30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2D6R5/btrE4Bf2vbl/SZUVVmsXjyKDA6E9S7Oe30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2D6R5%2FbtrE4Bf2vbl%2FSZUVVmsXjyKDA6E9S7Oe30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1179&quot; height=&quot;815&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.16.02.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;docker build, push 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;registry 로그인 했으니까 이제 도커 이미지를 build하고 push 하도록 한다.&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;Docker meta 는 도커 이미지를 빌드 할 때 어떤 tag 를 사용할 것인지를 정하는 것으로 Docker meta action 말고 다른 (태그를 추출하는) 액션을 사용해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 metadata-action 의 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;branch 로 trigger 되면 raw 값에 따라 해당 브랜치 명이 그대로 들어가게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들면 docker-sample 라는 이름의 브랜치가 push 될 경우 / 그리고 push trigger 가 있을 경우&lt;/li&gt;
&lt;li&gt;&amp;lt;docker image&amp;gt;:docker-sample 이라는 이름으로 태그가 이미지에 붙게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;major, minor 의 경우 버젼으로 release(tag 생성) 로 trigger 가 발생되면 해당 버젼이 들어가게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v0.1.2 로 태그 (여기서는 git tag) 가 생성되면 v0, v0.1 의 태그 2개가 생성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 밖에도 latest 라는 태그를 붙일 수 있고, enable 이라는 키에 true, false 값을 넣어서 특정 태그를 무조건 붙이게 할 수도 있다. 더 자세한 태그 생성 방식은 github 에서 확인하도록 하자 &lt;a href=&quot;https://github.com/docker/metadata-action&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/docker/metadata-action&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655468307113&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{raw}}
            type=semver,pattern=v{{major}}
            type=semver,pattern=v{{major}}.{{minor}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tag 생성을 했으면 해당 tag 를 도커 이미지에 붙이고 push 하도록 한다. (정확히는 build and push)&lt;/p&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 build 할 때 사용하는 옵션은 with 라는 값에 넣어서 쓸 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 여기서 tags 에 들어가는 값은 위에 작성하였던 metadata-action 의 결과 값으로, 위의 step 에서 id 를 meta 라고 작성했기 때문에 결과를 그대로 가져다가 쓸 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;meta 에서 생성된 tag 개수 만큼 (종류 만큼) registry 에 push 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1655468683061&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      - name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          file: /path/to/Dockerfile
          no-cache: true
          push: true
          tags: ${{ steps.meta.outputs.tags }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체로 보자면 아래 처럼 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1655468855012&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: CI test

on:
  push:
    branches:
      - 'master'
    tags:
      - 'v*'

  workflow_dispatch:

env:
  REGISTRY: REGISTRY_SAMPLE
  IMAGE_NAME: IMAGE_NAME_SAMPLE

jobs:
  docker:
    runs-on: self-hosted
    steps:
      - name: Login to d2hub
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.IMAGE_NAME }}
          flavor: |
            latest=auto
            prefix=
            suffix=
          tags: |
            type=ref,event=branch
            type=semver,pattern={{raw}}
            type=semver,pattern=v{{major}}
            type=semver,pattern=v{{major}}.{{minor}}
      - name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          file: /path/to/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 이제 여기까지만 보면 github action 으로 할 수 있는 기본은 다 했다고 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기타 등등&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;github action workflow badge 로 노출하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; workflow 결과 상태를 badge 로도 보여줄 수 있다. workflow 로 가서 우측의 Create status badge 를 눌러준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.31.45.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxAEfR/btrE5RWAED2/nZPhNuGEE5dciCLVAId38k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxAEfR/btrE5RWAED2/nZPhNuGEE5dciCLVAId38k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxAEfR/btrE5RWAED2/nZPhNuGEE5dciCLVAId38k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxAEfR%2FbtrE5RWAED2%2FnZPhNuGEE5dciCLVAId38k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1604&quot; height=&quot;467&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.31.45.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나오는 link 를 복사해서 내 프로젝트의 markdown 으로 붙여주면 된다. 보통은 README.md 에 복붙할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.31.51.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;447&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMa141/btrE3ImDU8L/7LGTTZqfjXC8ONXZ0Y7tf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMa141/btrE3ImDU8L/7LGTTZqfjXC8ONXZ0Y7tf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMa141/btrE3ImDU8L/7LGTTZqfjXC8ONXZ0Y7tf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMa141%2FbtrE3ImDU8L%2F7LGTTZqfjXC8ONXZ0Y7tf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;447&quot; data-filename=&quot;스크린샷 2022-06-17 오후 9.31.51.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;447&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;이제 진짜 github action 은 기본은 다 안다고 볼 수 있다.&lt;/p&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://docs.github.com/en/actions/learn-github-actions/contexts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github action 이 제공하는 다양한 context&lt;/a&gt; 를 사용하여 좀 더 생산성 높은 CI/CD 환경을 구축하는 것이다. 무슨 말인가 하면 github action 은 커밋 메시지, 커밋 시각, job 상태 등등 다양한 값들을 변수로 제공하고 있기 때문에 이 값을 사용해서 상태 알람을 보낸다던지, 아님 cron job 비스무리 하게 스케쥴러 작업을 돌린다던지 등등을 할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 나아가서는 나만의 github custom action 을 만들 수 있는데, github action 을 직접 만들려면 자바스크립트(또는 타입스크립트) 아니면 도커 컨테이너로 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터는 응용편이라서 다음 게시글에 작성 도전을...!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/contexts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.github.com/en/actions/learn-github-actions/contexts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakao.com/2022/05/06/github-actions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech.kakao.com/2022/05/06/github-actions/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL</category>
      <category>GitHub</category>
      <category>Github Actions</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/192</guid>
      <comments>https://pymoon.tistory.com/entry/github-action-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-1#entry192comment</comments>
      <pubDate>Fri, 17 Jun 2022 21:35:46 +0900</pubDate>
    </item>
    <item>
      <title>도커 컨테이너 어플리케이션을 호스트 머신의 유저로 실행하기</title>
      <link>https://pymoon.tistory.com/entry/%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EB%A8%B8%EC%8B%A0%EC%9D%98-%EC%9C%A0%EC%A0%80%EB%A1%9C-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너 어플리케이션을 호스트 머신의 유저로 실행하기&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너의 스프링 부트에서 로그를 호스트 머신의 디렉토리에 남기고 싶었다. 그리고 호스트 머신 유저랑 어플리케이션 유저가 항상 같다는 전제가 있음!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디렉토리 마운트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디렉토리를 rw 로 마운트 하였지만, (어플리케이션 로그는 작성이 되고) 톰캣 access log 는 &lt;code&gt;Permission denied&lt;/code&gt; 가 떴다. 디렉토리의 권한을 변경하면 해결되는 문제였지만, &lt;code&gt;777&lt;/code&gt; 은 뭔가 꺼려졌다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;priviledged&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안상 사용을 지양하고 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;호스트 머신의 유저로 실행하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리서치 한 방법으로는 2가지 방법이 있다.&lt;/p&gt;
&lt;ol 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;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도커 이미지 실행 시 호스트의 유저 정보로 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 실행 명령 시 호스트 머신의 &lt;code&gt;id&lt;/code&gt; 값을 넘기는 것이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;UID=$(id -u)
GID=$(id -g)
docker run -it -d --user ${UID}:${GID} &amp;lt;IMAGE NAME&amp;gt;&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;code&gt;bash&lt;/code&gt; 에 &lt;code&gt;UID&lt;/code&gt;, &lt;code&gt;GID&lt;/code&gt; 가 이미 설정 되어 있다면 warn 로그가 뜰 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도커 이미지 빌드 시 호스트의 유저 정보와 동일하게 추가하여 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 머신의 유저 정보를 이미 알고 있다고 가정 하에 해당 &lt;code&gt;UID&lt;/code&gt; 와 &lt;code&gt;GID&lt;/code&gt; 로 동일하게 유저 정보 추가해서 사용하는 것이다. (&lt;a href=&quot;https://busybox.net/downloads/BusyBox.html#adduser&quot;&gt;adduser 옵션 확인&lt;/a&gt;)&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;RUN addgroup -g &amp;lt;GID&amp;gt; -S &amp;lt;USER&amp;gt; &amp;amp;&amp;amp; adduser -u &amp;lt;UID&amp;gt; -D &amp;lt;USER&amp;gt; -G &amp;lt;USER&amp;gt; -H
USER &amp;lt;USER&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주저리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 도커는 어려워&lt;/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;a href=&quot;https://faun.pub/set-current-host-user-for-docker-container-4e521cef9ffc&quot;&gt;https://faun.pub/set-current-host-user-for-docker-container-4e521cef9ffc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jtreminio.com/blog/running-docker-containers-as-current-host-user/&quot;&gt;https://jtreminio.com/blog/running-docker-containers-as-current-host-user/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/45836272/running-as-a-host-user-within-a-docker-container&quot;&gt;https://stackoverflow.com/questions/45836272/running-as-a-host-user-within-a-docker-container&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL/도커 Docker</category>
      <category>docker</category>
      <category>trouble shotting</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/191</guid>
      <comments>https://pymoon.tistory.com/entry/%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EB%A8%B8%EC%8B%A0%EC%9D%98-%EC%9C%A0%EC%A0%80%EB%A1%9C-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0#entry191comment</comments>
      <pubDate>Thu, 3 Mar 2022 22:37:59 +0900</pubDate>
    </item>
    <item>
      <title>[HBase] NotServingRegionException 해결</title>
      <link>https://pymoon.tistory.com/entry/HBase-NotServingRegionException-%ED%95%B4%EA%B2%B0</link>
      <description>&lt;p&gt;org.apache.hadoop.hbase.NotServingRegionException: Region xxx is not online on ... 에러 발생&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;hbase table 이름도 로그에 있었기 때문에, 해당 table 을 HBase WebUI 로 확인해 봄&lt;/p&gt;
&lt;p&gt;살펴보니 Regions by Region Server 에서 내용이 없었음 (region is off line)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Screen Shot 2021-04-07 at 11.47.46 AM.png&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;563&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XnG9L/btq1ZMNOyFZ/yThjAYOwPDrgeJEQHXYvQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XnG9L/btq1ZMNOyFZ/yThjAYOwPDrgeJEQHXYvQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XnG9L/btq1ZMNOyFZ/yThjAYOwPDrgeJEQHXYvQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXnG9L%2Fbtq1ZMNOyFZ%2FyThjAYOwPDrgeJEQHXYvQ0%2Fimg.png&quot; data-filename=&quot;Screen Shot 2021-04-07 at 11.47.46 AM.png&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;563&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&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Region 을 다시 Region server 에 할당해줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;그러기 위해선 region_name 을 알아야 하는데, 역시나 HBase WebUI 에서 확인 가능함 (빨간색으로 칠한 부분이다!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Table Regions 에서 보여지는 Name 은 RegionInfo 로 아래와 같은 형식으로 이루어진 것임 (최신 버젼 &lt;a href=&quot;https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/RegionInfo.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RegionInfo&lt;/a&gt; 확인)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;table_name,start_key,timestamp.encoded_region_name.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 encoded_region_name 으로 region 을 다시 할당해주고, major compaction 을 돌렸다,&lt;/p&gt;
&lt;pre id=&quot;code_1617765699196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;assign 'acc1ad1b7962564fc3a43e5907e8db33'
major_compact 'test_table'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;근데 이렇게 해도 안되더라. 알고보니 오프라인 region 이 꽤 되었던 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하나하나 찾아서 할당해주긴 힘드니까 그냥 repair 를 함&lt;/p&gt;
&lt;pre id=&quot;code_1617765757175&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hbase hbck -repair&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hbase client 정상 동작 확인함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 막 해도 (?) 되는 이유는 서비스 클러스터가 아니여서 그랬던것이다. (그래서 major compaction 도 막하고...)&lt;/p&gt;
&lt;p&gt;서비스는 또 다르게 접근해야할수도?&lt;/p&gt;</description>
      <category>트러블 슈팅</category>
      <category>HBase</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/188</guid>
      <comments>https://pymoon.tistory.com/entry/HBase-NotServingRegionException-%ED%95%B4%EA%B2%B0#entry188comment</comments>
      <pubDate>Wed, 7 Apr 2021 12:25:07 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 더 맵게 (Python, Java)</title>
      <link>https://pymoon.tistory.com/entry/Programmers-%EB%8D%94-%EB%A7%B5%EA%B2%8C-Python-Java</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 맵게&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;programmers.co.kr/learn/courses/30/lessons/42626&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;숫자 목록이 주어지고 거기서 가장 낮은 숫자와, 그 다음 숫자 * 2를 더한 값을 다시 목록에 넣을 수 있는 횟수를 리턴하는 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;목록을 값에 추가하고, 나올 때 무조건 &lt;b&gt;작은 값이 먼저 나와야&lt;/b&gt; 된다는 조건이 있기 때문에 min-heap 을 생각하면 그 다음엔 알맞은 자료구조를 선택하여 구현하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬에선 heap, 자바에선 PriorityQueue 를 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;(만약 큰 수대로 나와야 한다면 값을 추가할 때 -1 을 곱해서 사용하면 된다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;heap 사용하여 풀이 (Python)&lt;/h3&gt;
&lt;pre id=&quot;code_1615031231285&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from heapq import heappush, heappop


def solution(scoville, K):
    answer = 0
    h = []
    for s in scoville:
    	# 힙에 값 추가
        heappush(h, s)
    
    # 힙에 있는 원소 개수
    # len(h) 는 오래 걸리는 연산이 아니지만 +,- 할 수 있는 값이여서 변수 remain 을 따로 둠
    remain = len(h)
    while remain &amp;gt; 1:
        s1 = heappop(h)
        # 힙에서 원소 개수 뺏기 때문에 -1
        remain -= 1
        # 힙에서 나온 값은 가장 작은 값이다.
        # 이 값이 K 보다 작은 경우
        if s1 &amp;lt; K:
        	# 두번 째로 스코빌 지수가 낮은 음식을 꺼낸다.
            # 음식을 꺼내고, 다시 집어 넣을 것이기 때문에 remain 은 값 변화가 없어서 무시함
            s2 = heappop(h)
            # 음식을 섞은 횟수 (정답) 증가
            answer += 1
            # 다시 음식 넣기
            heappush(h, s1 + (s2 * 2))
        else:
        	# 힙에서 나온 값이 K 이상이다.
            # 이러면 그 뒤의 값들은 무조건 K 이상이므로
            # 모든 원소가 K 보다 크다고 볼 수 있다. (음식의 스코빌을 섞을 필요가 없음)
            # 그래서 바로 정답을 리턴한다.
            return answer
    
    # remain 이 1일 때, 즉 힙에 남아 있는 음식이 한개일 때 여기로 오게 됨
    # 이 때 힙에 있는 유일한 음식이 K 이상의 스코빌 지수를 갖는다면
    if h and h[0] &amp;gt;= K:
    	# 정답을 리턴
        return answer
    # 힙에 남아 있는 음식이 K 미만인 경우엔
    # 더 이상 섞을게 없다는 의미이므로 -1 리턴
    return -1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;파이썬에서 heap 은 기본이 min-heap 이다.&lt;/p&gt;
&lt;p&gt;그래서 처음에 나온 값이 가장 낮은 값 인것이 보장이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 원소 두개를 빼고, 문제에서 주어진 식으로 (a[i] + (a[i+1] * 2)) 계산하여 다시 힙에 넣는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정답을 만드는 조건은, 다시 힙에 push 했을 때, 즉 두 음식의 스코빌 지수를 섞을 때이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 계속 음식을 섞다가 (힙에 넣고 빼고 하다가), 더 이상 섞을 게 없다는 의미는&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- 힙에 원소가 하나만 남거나 (1)&lt;/p&gt;
&lt;p&gt;- 힙에서 뺀 값이 스코빌 K 이상이 되는 경우다. (2)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(2) 는 힙에서 뺀 값이 K 이상이면 바로 answer 를 리턴 하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(1) 은 힙에 남아 있는 개수를 확인하며 while 루프를 돈다. while 루프를 다 도는 경우는 힙에 원소가 하나만 남는 경우로 이 때 하나만 남은 값이 K 미만이라면 음식을 모두 섞었음에도 모든 원소가 K 이상이 될 수 없다는 의미이다. 이 조건을 확인하여 -1 을 리턴하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;heap 사용하여 풀이 (Java)&lt;/h3&gt;
&lt;p&gt;자바로도 비슷하게 풀 수 있다. PriorityQueue 자료구조를 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1615031766316&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.PriorityQueue;


class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        PriorityQueue&amp;lt;Integer&amp;gt; heap = new PriorityQueue&amp;lt;&amp;gt;();

        for (int s: scoville) {
            heap.add(s);
        }

        while (heap.size() &amp;gt; 1) {
            int s1 = heap.poll();
            if (s1 &amp;gt;= K) {
                return answer;
            }
            int s2 = heap.poll();
            answer++;
            heap.add(s1 + (s2 * 2));
        }
        
        if (!heap.isEmpty() &amp;amp;&amp;amp; heap.peek() &amp;gt; K) {
            return answer;
        }

        return -1;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>문제 풀이/programmers</category>
      <category>programmer</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <category>프로그래머스</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/184</guid>
      <comments>https://pymoon.tistory.com/entry/Programmers-%EB%8D%94-%EB%A7%B5%EA%B2%8C-Python-Java#entry184comment</comments>
      <pubDate>Sat, 6 Mar 2021 20:56:36 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 가장 큰 수 (Python)</title>
      <link>https://pymoon.tistory.com/entry/Programmers-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98-Python</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;가장 큰 수&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;programmers.co.kr/learn/courses/30/lessons/42746&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;number 배열이 주어졌을 때 원소 조합으로 가장 큰 숫자를 만들어 리턴하는 문제다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;비슷한 문제 / 풀이가 Geeks for Geeks 에 있다.&amp;nbsp; 2가지 풀이 방법이 있는데&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째는 숫자를 문자열로 바꾸는 custom compare 함수를 구현하여 언어에서 제공하는 정렬 함수를 사용하는 것이다.&lt;/p&gt;
&lt;p&gt;두 번째는 가장 큰 숫자의 자릿 수 만큼 모든 숫자를 만든 다음에, 새롭게 만든 그 숫자들로 정렬하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Custom compare 함수 만들어 풀이&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/given-an-array-of-numbers-arrange-the-numbers-to-form-the-biggest-number/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.geeksforgeeks.org/given-an-array-of-numbers-arrange-the-numbers-to-form-the-biggest-number/&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614873050620&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import functools


def comp(n1, n2):
    if int(str(n1) + str(n2)) &amp;lt; int(str(n2) + str(n1)):
        return 1
    elif int(str(n1) + str(n2)) &amp;gt; int(str(n2) + str(n1)):
        return -1
    return 0


def solution(numbers):
    numbers.sort(key=functools.cmp_to_key(comp))

    return &quot;&quot;.join(str(_) for _ in numbers) if numbers[0] != 0 else &quot;0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;compare 함수를 따로 만들고 해당 함수로 정렬을 한다. python3 로 풀었지만, 다른 언어로 풀어도 비슷할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한 가지 엣지 포인트는 0 으로만 된 문자열일 경우 정답은 &quot;0&quot; 으로 리턴해야 한다는 것이다. (나도 여기서 틀렸는데 이게 프로그래머스 11번 케이스다. [0, 0, 0, 0] 과 같을 경우 &quot;0&quot; 으로 나와야 한다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;숫자를 가장 큰 수의 자릿수 만큼 반복하여 정렬하는 풀이&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/arrange-given-numbers-form-biggest-number-set-2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.geeksforgeeks.org/arrange-given-numbers-form-biggest-number-set-2/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가장 큰 수의 자릿수 만큼 숫자들을 반복하게 하고 (예를 들면 [9, 10, 2] 라고 했을 때 [99, 10, 22] 로 만든다는 의미) 그 값들로 정렬하여 문자열로 만드는 것이다. (실제로 풀이에선 가장 큰 수의 자릿수 + 1 만큼 반복하였다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;알고리즘을 (당연하게도) 바로 떠올린 것은 아니고 GFG 에 있는 그림을 보고 아 이렇게 할 수도 있는거구나! 해서 구현하였다. (세상에 천재들이 많다 ㅠㅠ)&lt;/p&gt;
&lt;pre id=&quot;code_1614873307105&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(numbers):

    new_numbers = []
    
    max_num = max(numbers)
    size = len(str(max_num)) + 1
   
    for num in numbers:
        new_numbers.append((num, str(num) * size))

    new_numbers.sort(reverse=True, key=lambda x: x[1])
    answer = []
    for nt in new_numbers:
        answer.append(str(nt[0]))

    return &quot;&quot;.join(answer) if answer[0] != &quot;0&quot; else &quot;0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;new_numbers 에는 원래 값 num 과 새로 만든 값 (size+1 만큼 반복한 문자열 num) 을 넣었고, 정렬할 때는 문자열로 만든 값을 사용하고 정답을 만들 때는 num 을 썼다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문자열 str(num) 을 만들 때 (size + 1) 을 곱하는 이유는 151, 15 와 같은 값이 있을 때 size (3) 만큼 반복하게 되면 151, 151 이 나온다.&lt;/p&gt;
&lt;p&gt;이럴 경우 문자열로 정렬하게 되면 151, 151 은 같기 때문에, 정답이 15115 가 나올 수도 있다. (실제 정답은 15151 이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 때문에 아마 다른 답변을 살펴보면 +1 만큼 반복하게 만든 것이 꽤 있을 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;풀고 난 다음에, 프로그래머스 정답을 맞추게 되면 다른 사람 풀이도 볼 수 있는데 제일 투표(?) 많이 받은 답이 바로 이거다. (진짜 짧다! 와우)&lt;/p&gt;
&lt;pre id=&quot;code_1614873539260&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(numbers):
    numbers = list(map(str, numbers))
    numbers.sort(key=lambda x: x*3, reverse=True)
    return str(int(''.join(numbers)))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이게 결국 GFG Hard 풀이인, 숫자 자릿수 만큼 반복(*3) 해서 만들어 정렬 하는 답이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내 풀이와 다른 점은&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 문제에서 number 가 1000 이하이기 때문에 일괄로 3을 곱한 것이고&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 나는 new_numbers 에 튜플을 넣어서 사용해서 좀 복잡해졌는데, 저 풀이는 정렬의 키에서만 문자 반복을 사용했기 때문에 numbers 를 그대로 써도 되었다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 프로그래머스 답변을 참고해서 바꿔보면 이렇게 바꿀수도 있었다. (최종_진짜_최종_최종)&lt;/p&gt;
&lt;pre id=&quot;code_1614877486670&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(numbers):
    max_num = max(numbers)
    size = len(str(max_num)) + 1
    snums = []
    for num in numbers:
        snums.append(str(num))

    snums.sort(reverse=True, key=lambda x: x * size)
    return &quot;&quot;.join(snums) if snums[0] != &quot;0&quot; else &quot;0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로 프로그래머스가 정답 셋이 적어서 GFG (&lt;a href=&quot;https://practice.geeksforgeeks.org/problems/largest-number-formed-from-an-array1117/1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;practice.geeksforgeeks.org/problems/largest-number-formed-from-an-array1117/1&lt;/a&gt;) 에서도 풀이를 제출해보는 것을 권장한다.&lt;/p&gt;</description>
      <category>문제 풀이/programmers</category>
      <category>programmers</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <category>프로그래머스</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/183</guid>
      <comments>https://pymoon.tistory.com/entry/Programmers-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98-Python#entry183comment</comments>
      <pubDate>Fri, 5 Mar 2021 01:05:48 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 주식가격 (Python)</title>
      <link>https://pymoon.tistory.com/entry/Programmers-%EC%A3%BC%EC%8B%9D%EA%B0%80%EA%B2%A9-Python</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;주식가격&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42584?language=python3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;programmers.co.kr/learn/courses/30/lessons/42584&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;알고리즘 문제 풀이에 자주 보이는, next greater element 랑 비슷한 문제다.&lt;/p&gt;
&lt;p&gt;주어진 배열에서 각 인덱스가 i &amp;lt; j 일때 A[i] 와 A[j] 간을 비교하여 j - i 만큼을 정답에 추가하는 문제다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;빠르게 하려고 파이썬으로 풀어보았다. (&lt;span style=&quot;color: #333333;&quot;&gt;파이썬에서 list 는 스택과 동일하므로 list 를 사용하였다.)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614866526769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(prices):
    # 정답의 전체 길이는 prices 와 동일하다. 그래서 prices 길이만큼 0 으로 초기화 시켜주었다.
    answer = [0 for _ in range(len(prices))]
    stack = list()
    # 0 번쨰 값은 미리 스택에 넣어두었다.
    stack.append(0)
    
    # prices 를 인덱스 접근으로 순회
    for index in range(1, len(prices)):
        # 스택에 값이 있고, 스택의 가장 마지막 (last) 값이 현재 price 보다 크다면 주식 값이 떨어진 것이다.
        # 스택의 가장 마지막 값은 인덱스 값이므로 prices[stack[-1]] 로 비교하였다.
        while stack and prices[stack[-1]] &amp;gt; prices[index]:
            # 현재 price 보다 큰 주식 을 찾는다.
            # 바꿔 말하면 index 시점에서 주식이 떨어졌다고 봐도 되는 index 이전의 모든 주식을 찾는다는 얘기다.
            # index 이전의 모든 주식은 stack 에 넣어놨기 때문에 stack 을 반복문으로 돈다.
            prev = stack.pop()
            # prev 는 index 이전의 어느 값이다
            # 조건에선 단순 비교를 했고 여기서 실제로 pop 을 해 준다. (파이썬에선 자바의 poll, peek 메서드가 없기 때문)
            # prev 시점에 샀던 주식 &amp;gt; index 시점의 주식이 된다.
            # 이 인덱스 간의 차이가 기간 (초) 가 된다.
            answer[prev] = (index - prev)
        
        # 현재 주식에 해당하는 index를 넣어준다.
        stack.append(index)
    
    # 여기까지 왔으면 prices 를 전부 순회하였다.
    for i in range(len(answer)):
        # answer[i] 가 0 이라는 의미는 i 시점에 한번도 주식이 떨어지지 않았다는 것이다.
        if answer[i] == 0:
            # 그러면 len(prices) 만큼의 시간 동안 끝까지 떨어지지 않았다는 의미이므로
            # 전체 prices 길이 - i - 1 을 업데이트 해준다.
            answer[i] = len(prices) - i - 1
    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;최대한 주석에 풀이를 작성하려고 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;정답 answer 는 전체 배열의 크기와 동일하므로 0으로 prices 사이즈만큼 초기화 시켜주었다. 그리고 첫번째 원소 값은 미리 스택에 넣어두었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;스택에 있는 값은 인덱스과 각 인덱스에 해당하는 prices 값으로, prices 를 순회하면서 스택에 있는 last 값과 비교를 하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;스택에 있는 가장 마지막 값은 index 시점 보다 이전의 값으로 index - x (코드에선 prev) 에 해당하는 어떤 지점일 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 값과 현재 prices 를 비교하여 주식이 떨어졌는지 보고, 떨어졌다면 answer 에 각 인덱스 차이 만큼을 넣어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;스택을 계속 순회하는 이유는 [1, 3, 3, 2, 2] 같은 예제로 보자면, 2번째 주식과 3번째 주식을 비교하여 2번째 주식이 떨어졌음은 확인 가능하지만 1번째 주식이 3번째 시점에 떨어졌는지는 모르기 때문이다. 스택에 있는 값의 가장 마지막 값을 계속 pop 하여서 비교해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;예제에서 [1, 2, 3, 2, 3] 가 주어졌을 때, prices 를 다 순회한 시점에서 answer 는 [0, 0, 1, 0, 0] 이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 나머지 0 으로 되어 있는 부분에 해당하는 인덱스의 prices 는 한 번도 주식 이 떨어지지 않았다는 것이다. 그래서 전체 prices 길이 5에서 각 인덱스 -1 을 빼 주면 그 값에 해당하는 초 만큼 떨어지지 않았다고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;말이 좀 어렵지만, 결국 answer[0] 은 전체&amp;nbsp; prices 길이 5 - 0 - 1 을 했고 그 값인 4가 나왔다는 것이다. 그러면 answer[0] 은 4초동안 주식이 떨어지지 않았다는 것을 의미한다.&lt;/p&gt;</description>
      <category>문제 풀이/programmers</category>
      <category>programmers</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <author>민Z</author>
      <guid isPermaLink="true">https://pymoon.tistory.com/182</guid>
      <comments>https://pymoon.tistory.com/entry/Programmers-%EC%A3%BC%EC%8B%9D%EA%B0%80%EA%B2%A9-Python#entry182comment</comments>
      <pubDate>Thu, 4 Mar 2021 23:20:02 +0900</pubDate>
    </item>
  </channel>
</rss>