<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발하는 개구리</title>
    <link>https://99uulog.tistory.com/</link>
    <description>웹 &amp;middot; 모바일 앱 &amp;middot; 데스크탑 앱 &amp;middot; UI/UX 디자인 &amp;middot; PS 합니다.</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 12:57:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>중규리</managingEditor>
    <image>
      <title>개발하는 개구리</title>
      <url>https://tistory1.daumcdn.net/tistory/5867396/attach/2f93cb6418b54d499198f25288fd5bd6</url>
      <link>https://99uulog.tistory.com</link>
    </image>
    <item>
      <title>[EAS-iOS Build] expo 빌드 + 제출 과정에서 발생하는 다양한 에러 대응 방법</title>
      <link>https://99uulog.tistory.com/135</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Slide 16_9 - 34.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G1Zz4/btsHcRz9BcV/qMRNcttwjnP0T6KH3YwMlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G1Zz4/btsHcRz9BcV/qMRNcttwjnP0T6KH3YwMlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G1Zz4/btsHcRz9BcV/qMRNcttwjnP0T6KH3YwMlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG1Zz4%2FbtsHcRz9BcV%2FqMRNcttwjnP0T6KH3YwMlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;650&quot; data-filename=&quot;Slide 16_9 - 34.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;650&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;EAS 빌드 과정 중 발생한 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Application&amp;nbsp;Loader&amp;nbsp;Error&amp;nbsp;Output]:&amp;nbsp;Asset&amp;nbsp;validation&amp;nbsp;failed&amp;nbsp;SDK&amp;nbsp;version&amp;nbsp;issue.&amp;nbsp;This&amp;nbsp;app&amp;nbsp;was&amp;nbsp;built&amp;nbsp;with&amp;nbsp;the&amp;nbsp;iOS&amp;nbsp;16.1&amp;nbsp;SDK.&amp;nbsp;All&amp;nbsp;iOS&amp;nbsp;and&amp;nbsp;iPadOS&amp;nbsp;apps&amp;nbsp;must&amp;nbsp;be&amp;nbsp;built&amp;nbsp;with&amp;nbsp;the&amp;nbsp;iOS&amp;nbsp;17&amp;nbsp;SDK&amp;nbsp;or&amp;nbsp;later,&amp;nbsp;included&amp;nbsp;in&amp;nbsp;Xcode&amp;nbsp;15&amp;nbsp;or&amp;nbsp;later,&amp;nbsp;in&amp;nbsp;order&amp;nbsp;to&amp;nbsp;be&amp;nbsp;uploaded&amp;nbsp;to&amp;nbsp;App&amp;nbsp;Store&amp;nbsp;Connect&amp;nbsp;or&amp;nbsp;submitted&amp;nbsp;for&amp;nbsp;distribution.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fastlane&amp;nbsp;pilot&amp;nbsp;failed&amp;nbsp;fastlane&amp;nbsp;exited&amp;nbsp;with&amp;nbsp;non-zero&amp;nbsp;code:&amp;nbsp;1&amp;nbsp;Failed&amp;nbsp;to&amp;nbsp;submit&amp;nbsp;the&amp;nbsp;app&amp;nbsp;to&amp;nbsp;the&amp;nbsp;store&lt;/p&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;Build&amp;nbsp;failed:&amp;nbsp;Your&amp;nbsp;project&amp;nbsp;requires&amp;nbsp;a&amp;nbsp;newer&amp;nbsp;version&amp;nbsp;of&amp;nbsp;CocoaPods&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;&amp;nbsp;from /Users/expo/workingdir/build/TERMTERM/ios/Podfile:47&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;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;use_react_native!(&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:path =&amp;gt; config[:reactNativePath],&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;-&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error:&amp;nbsp;Cannot&amp;nbsp;find&amp;nbsp;module&amp;nbsp;'string-width'&lt;/p&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;const&amp;nbsp;stringWidth&amp;nbsp;=&amp;nbsp;require('string-width');&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;발생 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Expo SDK 46&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;우선 Expo SDK는 iOS SDK와 직결되기 때문에 최신 버전을 따라가는게 베스트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 deprecated된 버전을 이용한다면 최신 버전의 시뮬레이터에서 실행이 안되고, 빌드가 되더라도 appstoreconnect에 submission할 수 없다.&lt;/p&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;figure id=&quot;og_1714899718221&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;Upgrade Expo SDK&quot; data-og-description=&quot;Learn how to incrementally upgrade the Expo SDK version in your project.&quot; data-og-host=&quot;docs.expo.dev&quot; data-og-source-url=&quot;https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/&quot; data-og-url=&quot;https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLaXZT/hyVZknW5dG/0zN4Y8KOqF9qdhCKb4ciqK/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/mSNz3/hyVZgMD4kQ/NA6mKMleL7KPZOYTHTgw6k/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bKgqMe/hyVZi4L8we/axST9mY0H6GkOqMkJk4Qr1/img.png?width=620&amp;amp;height=620&amp;amp;face=0_0_620_620&quot;&gt;&lt;a href=&quot;https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLaXZT/hyVZknW5dG/0zN4Y8KOqF9qdhCKb4ciqK/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/mSNz3/hyVZgMD4kQ/NA6mKMleL7KPZOYTHTgw6k/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bKgqMe/hyVZi4L8we/axST9mY0H6GkOqMkJk4Qr1/img.png?width=620&amp;amp;height=620&amp;amp;face=0_0_620_620');&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;Upgrade Expo SDK&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to incrementally upgrade the Expo SDK version in your project.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.expo.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웬만하면 문서의 모든 절차를 따라해서 모듈의 버전까지 잘 맞춰주도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 SDK를 업데이트하면, 각종 에러와 만나게 될 것이다. 특히 모듈을 덕지덕지 가져다 쓴 프로젝트는 에러의 정도가 심할 것...&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-05-05 오후 6.03.06.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;1304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dw61cQ/btsHbRgyApZ/ImrRVxaALIw1mMks7YmPpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dw61cQ/btsHbRgyApZ/ImrRVxaALIw1mMks7YmPpk/img.png&quot; data-alt=&quot;그게 바로 저예요...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dw61cQ/btsHbRgyApZ/ImrRVxaALIw1mMks7YmPpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdw61cQ%2FbtsHbRgyApZ%2FImrRVxaALIw1mMks7YmPpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;397&quot; data-filename=&quot;스크린샷 2024-05-05 오후 6.03.06.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;1304&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그게 바로 저예요...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여튼 이때 각종 문제가 발생하고 로그 길이는 몇만줄이고... 멘탈 가루가 되지만 빌드를 웹에 계속 올려서 돌려보지 말고 아래 순서를 따라 로컬에서 문제를 먼저 파악하는게 좋다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;npx react-native-clean-project&lt;/li&gt;
&lt;li&gt;npx expo-doctor&lt;/li&gt;
&lt;li&gt;npx expo install --fix&lt;/li&gt;
&lt;li&gt;npx expo prebuild --clean&lt;/li&gt;
&lt;li&gt;yarn ios&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 에러가 뜨면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;웬만하면 &lt;/span&gt;eas에서도 빌드가 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 발생하는 에러를 잘 처리하면, 웬만하면 빌드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1714899965440&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;SDK 49 - eas build ios failed on &amp;quot;run fastlane&amp;quot; &amp;middot; Issue #2109 &amp;middot; expo/eas-cli&quot; data-og-description=&quot;Build/Submit details page URL https://expo.dev/accounts/johnfa/projects/carles-connect/builds/5f04dc92-7dbc-42c8-9f9f-bb1dac0c7b2c#run-fastlane Summary We started a new project with expo 49. It err...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/expo/eas-cli/issues/2109&quot; data-og-url=&quot;https://github.com/expo/eas-cli/issues/2109&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eopZrL/hyVZeOOJqy/CHMtwGnKfSenQoExJaLtzK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/expo/eas-cli/issues/2109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/expo/eas-cli/issues/2109&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eopZrL/hyVZeOOJqy/CHMtwGnKfSenQoExJaLtzK/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;SDK 49 - eas build ios failed on &quot;run fastlane&quot; &amp;middot; Issue #2109 &amp;middot; expo/eas-cli&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Build/Submit details page URL https://expo.dev/accounts/johnfa/projects/carles-connect/builds/5f04dc92-7dbc-42c8-9f9f-bb1dac0c7b2c#run-fastlane Summary We started a new project with expo 49. It err...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDK 업데이트하면서 빌드 지옥에 빠진 사람들이 아주 많다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나같은 경우에는 string-width?라는 모듈에서 문제가 있었는데, 해당 모듈은 직접 사용한게 아니라 다른 모듈들의 peer 종속성이라 자동으로 설치된 경우여서... 뭐 내가 임의로 코드를 건들 수 있는 상황이 아니기 때문에 에러를 해결하는 데에 더더 더 더더더더더더 더더더더더더 오래걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의로 할 수 있는 해결 방법은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. package.json에 아래 문구 추가&lt;/p&gt;
&lt;pre id=&quot;code_1714900127065&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &quot;resolutions&quot;: {
    &quot;jackspeak&quot;: &quot;2.1.1&quot;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. yarn.lock 제거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. yarn install 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 다시 eas 빌드를 돌려보면 정상적으로 수행된다.&lt;/p&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;We've&amp;nbsp;lost&amp;nbsp;connection&amp;nbsp;to&amp;nbsp;the&amp;nbsp;worker.&lt;br /&gt;&lt;br /&gt;Two&amp;nbsp;common&amp;nbsp;reasons:&lt;br /&gt;1.&amp;nbsp;Worker&amp;nbsp;ran&amp;nbsp;out&amp;nbsp;of&amp;nbsp;memory.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Try&amp;nbsp;running&amp;nbsp;your&amp;nbsp;build&amp;nbsp;on&amp;nbsp;a&amp;nbsp;large&amp;nbsp;worker.&amp;nbsp;Learn&amp;nbsp;more&amp;nbsp;at&amp;nbsp;&lt;a href=&quot;https://docs.expo.dev/build/eas-json/#selecting-resource-class&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.expo.dev/build/eas-json/#selecting-resource-class&lt;/a&gt;&lt;br /&gt;2.&amp;nbsp;Worker&amp;nbsp;experienced&amp;nbsp;a&amp;nbsp;network&amp;nbsp;issue.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;This&amp;nbsp;is&amp;nbsp;most&amp;nbsp;likely&amp;nbsp;an&amp;nbsp;intermittent&amp;nbsp;problem.&amp;nbsp;Try&amp;nbsp;running&amp;nbsp;the&amp;nbsp;build&amp;nbsp;again.&lt;/p&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;침착하게 캐시지우고 rebuild해주면 운좋게 성공할 수 있다... 돈 안낼거면 CPU 안터지는 시간에 운에 기대서 빌드 돌려야할둣^^,,&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;더이상 expo go와 EAS를 사용할 일은 제 인생에 없을 것입니다.&lt;/p&gt;</description>
      <category>개발/frontend</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/135</guid>
      <comments>https://99uulog.tistory.com/135#entry135comment</comments>
      <pubDate>Sun, 5 May 2024 18:11:41 +0900</pubDate>
    </item>
    <item>
      <title>RabbitMQ(2) - RabbitMQ, GemFire 개념 및 등장 배경</title>
      <link>https://99uulog.tistory.com/134</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rabbitmq.png&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EJLhI/btsGJsAwaQC/hiwjw6GfoytZvs0km9Kxm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EJLhI/btsGJsAwaQC/hiwjw6GfoytZvs0km9Kxm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EJLhI/btsGJsAwaQC/hiwjw6GfoytZvs0km9Kxm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEJLhI%2FbtsGJsAwaQC%2Fhiwjw6GfoytZvs0km9Kxm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1926&quot; height=&quot;878&quot; data-filename=&quot;rabbitmq.png&quot; data-origin-width=&quot;1926&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  모놀리식 아키텍쳐&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 어플리케이션에 여러가지 비즈니스 서비스가 번들되는 형태&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적인 모놀리식 아키텍쳐에선 개발팀의 규모가 컸다.&lt;/li&gt;
&lt;li&gt;결합도가 너무 높았음 (리스크 높음)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;개발자간 지식을 공유하는 것도 어려웠다.&lt;/li&gt;
&lt;li&gt;데이터 거버넌스 세팅을 공유하는 것도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;레거시 기술에 국한될 수 밖에 없음
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;신기술 도입이나, 레거시에 알맞는 개발자를 찾기도 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  모던 소프트웨어 아키텍쳐&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독립적인 각각의 Micro Service를 구축해서, 상호 작용하도록 만든 형태&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;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;독립적으로 개발되고, 테스트되고, 배포되기 때문에 빠르다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터가 늘어나는 상황에서 서비스를 스케일업하는 데에 강점을 가짐
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;테스트나 배포도 마찬가지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인력이나 신기술 도입 면에서도 강점을 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  GemFire&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key:value 형태의 응답 속도가 밀리세컨드 수준의 데이터 스토어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 속도가 필요한 경우는 ux와 직결됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실시간에 가까운 서비스의 경우, 액션이 즉각적으로 일어나길 기대하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MSA의 경우 독립적인 데이터베이스를 갖추고 있는데, 뱅킹 트랜잭션 같은 곳에서 상호간의 데이터를 참조하는 데에 시간을 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;예를 들어, 이상거래의 가능성을 밀리세컨드 수준으로 탐지할 수 있어야한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;즉각적으로 알람이 가고, 트랜잭션을 막아버리는 행위가 진행될 수 있어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기존의 비즈니스 로직은 수정하기 어렵고, 기술적인 향상으로 성능을 이끌어내야하는 상황에서 도움이 됨&lt;/li&gt;
&lt;li&gt;뱅킹 시스템 중 예시) 인출 장소 근처의 소비 장소를 추천해주는 형태
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;결국 전부 실시간성을 강조하고 싶은 것 같음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  RabbitMQ&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 메시지 브로커이며, 정보를 전달하는 역할을 담당함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 매체와 디바이스에서 데이터를 전달하기 위한 미들웨어로 사용된다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;빠르고, 안전하고, 신뢰성있게 전달할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;중요한 정보를 전달하는 상황에서, 여러 사용자와 매체, 상황에 대해 시스템 간 연결이 필요하다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이런 모든 경우를 범용할 수 있는 공통의 언어를 제공한다.&lt;/li&gt;
&lt;li&gt;한개의 메시지에 여러가지 액션이 붙어야하는 상황이 발생하기도 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;빠르고 안전하게 메시지를 전달하기 위해
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;안정성도 있어야함 (공용 와이파이를 이용할 때에도 기밀성을 유지해야함)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  RabbitMQ + GemFire&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 빠르고 안전하게 전달하는 RabbitMQ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 데이터를 분석하고 액세스하는 Gemfire&lt;/p&gt;</description>
      <category>기술/학습</category>
      <category>Gemfire</category>
      <category>rabbitmq</category>
      <category>메시지큐</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/134</guid>
      <comments>https://99uulog.tistory.com/134#entry134comment</comments>
      <pubDate>Wed, 17 Apr 2024 18:19:31 +0900</pubDate>
    </item>
    <item>
      <title>RabbitMQ(1) - RabbitMQ 기초, Kafka와 차이점</title>
      <link>https://99uulog.tistory.com/133</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rabbitmq.png&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byIDlI/btsGBxpBwOq/eaRpBrI0LK7Sp8szeyWUFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byIDlI/btsGBxpBwOq/eaRpBrI0LK7Sp8szeyWUFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byIDlI/btsGBxpBwOq/eaRpBrI0LK7Sp8szeyWUFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyIDlI%2FbtsGBxpBwOq%2FeaRpBrI0LK7Sp8szeyWUFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1926&quot; height=&quot;878&quot; data-filename=&quot;rabbitmq.png&quot; data-origin-width=&quot;1926&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 회사에서 3일간 RabbitMQ 교육을 다녀오게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내일부터 시작되는 교육 전, 사전에 학습한 내용을 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;메시지 큐&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lvye9/btsGCwjb1Y1/dXf3UFUXGH4zwZfYm8Wb3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lvye9/btsGCwjb1Y1/dXf3UFUXGH4zwZfYm8Wb3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lvye9/btsGCwjb1Y1/dXf3UFUXGH4zwZfYm8Wb3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flvye9%2FbtsGCwjb1Y1%2FdXf3UFUXGH4zwZfYm8Wb3K%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;443&quot; height=&quot;180&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;180&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;RabbitMQ, Redis, Kafka.. 모두 메시지 큐 기반의 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐란, 프로세스 또는 프로그램 인스턴스가 데이터를 교환할 때 사용하는 통신 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 &lt;b&gt;정보를 제공하는 Producer&lt;/b&gt;, &lt;b&gt;정보가 잠시 담겨있는 Queue&lt;/b&gt;, &lt;b&gt;정보를 제공받는 Consumer&lt;/b&gt; 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐의 장점은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비동기(Asynchronous)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Queue가 있기 때문에 비동기 처리하며 병목현상 방지 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;낮은 결합도(Decoupling)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보를 제공하는 서비스와 받는 서비스가 분리되며 결합도가 낮아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성(Scalable)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다수의 프로세스가 메시지 큐를 통해 메시지를 전달할 수 있기 때문에 확장성이 뛰어나다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탄력성(Resilience)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 Queue에 담기기 때문에, 서비스가 중지되어도 Queue가 중지된 것은 아니기 때문에 Queue에 있는 메시지를 후에 다시 처리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보장성(Guarantees)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Queue에 보관된 메시지가 Consumer에게 전달된다는 보장을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;기본적으로 RabbitMQ는 AMQP(Advanced Message Queuing Protocol) 기반의 &lt;b&gt;메시지 브로커&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 많거나, 요청 처리 시간일 길 때, 해당 요청을 다른 API에게 넘기고 빠른 응답을 할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AMQP란, 메&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜을 뜻한다. 쉽게 말해서, &lt;/span&gt;&lt;b&gt;메시지 큐(MQ) 오픈소스의 표준 프로토콜&lt;/b&gt;이라고 할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 빠른 것 같지만, RabbitMQ와 Kafka의 결정적인 차이점이 바로 RabbitMQ가 메시지 브로커라는 점이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RabbitMQ, Redis는 메시지 브로커&lt;/b&gt;이며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Kafka는 이벤트 브로커&lt;/b&gt;이다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=H_DaPyUOeTo&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/YSRPT/hyVPUvp3hb/Y9mCHiqjL6SEys4Ag7OXv1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;카프카, 레빗엠큐, 레디스 큐의 큰 차이점! 이벤트 브로커와 메시지 브로커에 대해 알아봅시다.&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/H_DaPyUOeTo&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 메시지 브로커는 이벤트 브로커의 역할을 할 수 없지만, 반대로 이벤트 브로커는 메시지 브로커의 역할을 할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메시지 브로커의 경우, 많은 기업의 대규모 메시지 기반 미들웨어 아키텍쳐에서 사용되어왔다. 기본적으로 큐에 메시지를 보내고 받으며 프로듀서와 컨슈머 간 네트워크를 구성하기 위해 사용되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메시지 브로커의 특징은 &lt;b&gt;메시지를 처리하면 즉시 또는 짧은 시간 안에 메시지가 삭제되는 구조&lt;/b&gt;라는 점이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이벤트 브로커의 경우, 이벤트(메시지, 레코드)를 하나만 보관하고 인덱스를 통해 개별 액세스를 관리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;메시지 브로커와 다르게, 필요한 시간 동안 이벤트를 보존&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉 메시지 브로커는 메시지를 보내고 처리하고 삭제하지만, 이벤트 브로커는 삭제하지 않는다는 점이 가장 큰 차이점이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;본 포스팅은 Kafka가 아닌 RabbitMQ에 대한 내용이므로, 영상에서 언급되는 이벤트 브로커에 대한 자세한 내용은 생략했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개념&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UdDdu/btsGB0SDRmw/hV7umtDTT8l5ru1tAKQNT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UdDdu/btsGB0SDRmw/hV7umtDTT8l5ru1tAKQNT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UdDdu/btsGB0SDRmw/hV7umtDTT8l5ru1tAKQNT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUdDdu%2FbtsGB0SDRmw%2FhV7umtDTT8l5ru1tAKQNT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1206&quot; height=&quot;736&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 그림은 RabbitMQ의 전반적인 동작 방식을 나타낸 그림이다. 메시지 큐의 동작 방식 사이에 RabbitMQ만의 개념이 약간 추가되었다.&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;Producer&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐에서 다룬 키워드이지만, RabbitMQ에서는 Producer가 메시지를 Queue가 아닌 &lt;b&gt;Exchange&lt;/b&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;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Exchange&lt;/span&gt;&lt;/b&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;Producer로부터 전달 받은 정보를 어떤 Queue에 담을지 결정한다.&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;b&gt;총 4가지 타입&lt;/b&gt;이 있으며, Router와 같다고 생각하면 된다.&lt;/span&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;Direct, Topic, Headers, Fanout&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Binding&lt;/span&gt;&lt;/b&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;Exchange에게 메시지를 라우팅할 규칙을 지정하는 행위이다.&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;특정 조건에 맞는 메시지를 특정 큐에 전송하도록 하며, Exchange의 타입에 맞게 설정되어야한다.&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;Exchange와 Queue는 m:n 바인딩이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Queue&lt;/span&gt;&lt;/b&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;메시지(정보)를 일시적으로 저장하는 장소이다.&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;Queue는 호스트의 디스크 용량 및 메모리에 한정된다.&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;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Consumer&lt;/span&gt;&lt;/b&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;메시지를 수신하는 주체이며, Consumer들은 Queue에서 저장된 메시지를 읽는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Exchange Types&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Exchange에는 총 4가지 타입이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;1️⃣ Direct&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b42fxb/btsGC1cc74e/5jHsVmooUjJtpe40n1ADfk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b42fxb/btsGC1cc74e/5jHsVmooUjJtpe40n1ADfk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b42fxb/btsGC1cc74e/5jHsVmooUjJtpe40n1ADfk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b42fxb/btsGC1cc74e/5jHsVmooUjJtpe40n1ADfk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;810&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&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;메시지에 포함된 Routing Key를 바탕으로 Queue에 메시지를 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Queue에 같은 Routing Key를 지정해서 Fanout처럼 동작하도록 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Default Exchange는 이름이 없는 Direct Exchange이며,&lt;span&gt; RabbitMQ에서 생성되는 Queue가 자동으로 바인딩 되는데, 이때 각 Queue의 이름이 Routing Key로 설정된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;2️⃣ Topic&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1FbeU/btsGAP5D5xt/7QA6ihJv4oyeXW6x0LTyN0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1FbeU/btsGAP5D5xt/7QA6ihJv4oyeXW6x0LTyN0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1FbeU/btsGAP5D5xt/7QA6ihJv4oyeXW6x0LTyN0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/1FbeU/btsGAP5D5xt/7QA6ihJv4oyeXW6x0LTyN0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;810&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&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;Routing Key 패턴이 일치하는 모든 Queue에 메시지를 라우팅한다. 여러 Consumer에서 메시지 형태에 따라 선택적으로 수신해야하는 경우와 같이, Publish/Subscribe 패턴에서 사용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 패턴이 여러개 일치하더라도 &lt;b&gt;하나의 큐에는 한개의 메시지만 전달된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Binding Keyy는 온점(.)으로 구분된 단어의 조합으로 정의되고, (*)과 (#)을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(*) : 정확히 하나의 단어만 대체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(#) : 0개 혹은 여러 개의 단어 대체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;(*)과 (#) 같은 와일드카드는 Direct에서 지원되지 않는다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3️⃣ Headers&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfJGOe/btsGBhgf1Ho/sZqDRS0dS50u1YHa2aycKK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfJGOe/btsGBhgf1Ho/sZqDRS0dS50u1YHa2aycKK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfJGOe/btsGBhgf1Ho/sZqDRS0dS50u1YHa2aycKK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bfJGOe/btsGBhgf1Ho/sZqDRS0dS50u1YHa2aycKK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;810&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&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;Topic과 유사하지만, Header를 이용하기 때문에 더 다양한 속성을 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Key 속성은 무시되지만, headers 속성이 바인딩 시 지정된 값과 같은 경우일 때 같은 경우에 일치하는 것으로 간주된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3️⃣ Fanout&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Oxp7I/btsGBfv2F0d/bU5EETG4Nw7xrMFnyxX6A0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Oxp7I/btsGBfv2F0d/bU5EETG4Nw7xrMFnyxX6A0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Oxp7I/btsGBfv2F0d/bU5EETG4Nw7xrMFnyxX6A0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Oxp7I/btsGBfv2F0d/bU5EETG4Nw7xrMFnyxX6A0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;810&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;810&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;Routing key에 관계 없이, 모든 Queue에 동일한 메시지를 전달하는 브로드캐스트 라우팅 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dispatching&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다수의 Consumer가 1개의 Queue와 연결되어있다면, 라운드 로빈 방식으로 메시지를 분배한다.&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;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Fair Dispatching &amp;amp; Prefetch Count&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다수의 Consumer가 2개 이상의 Queue와 연결되어있다면, 라운드 로빈 방식으로 효율성을 보장할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령 홀수 번째 메시지 처리 시간이 매우 짧고, 짝수 번째 메시지 처리 시간이 매우 길다면 짝수 번째 메시지를 처리하는 Consumer에는 지속적으로 지연이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 RabbitMQ에는 &lt;b&gt;Prefetch Count&lt;/b&gt;라는 개념이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;u&gt;Queue에 메시지를 Consumer의 메모리에 쌓아둘 수 있는 최대 메시지의 양&lt;/u&gt;으로, Prefetch Conut의 값이 1로 설정된다면 하나의 메시지가 처리되기 전에는 새로운 메시지를 받지 않기 때문에 작업을 분산시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Message 보존&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RabbitMQ 서버가 재기동되면 Queue는 모두 제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 방지하기 위해 Queue 생성 시, Durable 옵션에 true를 설정하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer가 메시지를 발송할 때 PERSISTENT_TEXT_PLAIN 옵션을 주어야 메시지가 보존된다.&lt;/p&gt;
&lt;pre id=&quot;code_1713099866566&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Queue 생성, QueueName 다음이 durable option
rabbitmqChannel.queueDeclare(rabbitmqQueueName, true, false, false, null);

// 메시지 발송
rabbitmqChannel.basicPublish(exchangeName, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.cloudamqp.com/blog/what-is-message-queuing.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.cloudamqp.com/blog/what-is-message-queuing.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://somaz.tistory.com/119&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://somaz.tistory.com/119&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wildeveloperetrain.tistory.com/317&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wildeveloperetrain.tistory.com/317&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@cho876/%EC%B9%B4%ED%94%84%EC%B9%B4kafka-vs-RabbitMQ&quot;&gt;https://velog.io/@cho876/%EC%B9%B4%ED%94%84%EC%B9%B4kafka-vs-RabbitMQ&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.cloudamqp.com/blog/part1-rabbitmq-for-beginners-what-is-rabbitmq.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.cloudamqp.com/blog/part1-rabbitmq-for-beginners-what-is-rabbitmq.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.devops.dev/rabbitmq-exchange-types-explained-5fd9086595f5&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.devops.dev/rabbitmq-exchange-types-explained-5fd9086595f5&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;</description>
      <category>기술/학습</category>
      <category>AMQP</category>
      <category>Kafka</category>
      <category>mq</category>
      <category>rabbitmq</category>
      <category>REDIS</category>
      <category>메시지큐</category>
      <category>미들웨어</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/133</guid>
      <comments>https://99uulog.tistory.com/133#entry133comment</comments>
      <pubDate>Sun, 14 Apr 2024 22:05:45 +0900</pubDate>
    </item>
    <item>
      <title>백준 16168 퍼레이드 Python</title>
      <link>https://99uulog.tistory.com/132</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zrv6n/btsEQTvamfT/kk3wgHftwhxl5pXEwza2h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zrv6n/btsEQTvamfT/kk3wgHftwhxl5pXEwza2h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zrv6n/btsEQTvamfT/kk3wgHftwhxl5pXEwza2h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzrv6n%2FbtsEQTvamfT%2Fkk3wgHftwhxl5pXEwza2h1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707985952660&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;16168번: 퍼레이드&quot; data-og-description=&quot;첫 번째 줄에 지점의 개수 V, 연결 구간의 개수 E가&amp;nbsp;주어진다. (1 &amp;le;&amp;nbsp;V &amp;le;&amp;nbsp;E &amp;le;&amp;nbsp;3000) 이후 E개의 줄에 걸쳐 각 연결 구간이 연결하는 두 지점의 번호 Va, Vb가 공백을 사이에 두고 주어진다. (1 &amp;le;&amp;nbsp;Va,&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/16168&quot; data-og-url=&quot;https://www.acmicpc.net/problem/16168&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TTiLl/hyVjfOBK2y/KeudWBUtJDzxJKAT9PKpeK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16168&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/16168&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TTiLl/hyVjfOBK2y/KeudWBUtJDzxJKAT9PKpeK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;16168번: 퍼레이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 줄에 지점의 개수 V, 연결 구간의 개수 E가&amp;nbsp;주어진다. (1 &amp;le;&amp;nbsp;V &amp;le;&amp;nbsp;E &amp;le;&amp;nbsp;3000) 이후 E개의 줄에 걸쳐 각 연결 구간이 연결하는 두 지점의 번호 Va, Vb가 공백을 사이에 두고 주어진다. (1 &amp;le;&amp;nbsp;Va,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;b&gt;&lt;i&gt;오일러 경로 (유니온 파인드 + 그래프 탐색)&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;60m+ (중간에 오일러 경로의 조건을 찾아보느라 길어졌다.)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오일러 경로는 한 붓 그리기와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오일러 경로가 성립하기 위해서는 총 두가지 조건에 만족해야한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모든 정점이 연결되어있어야한다.&lt;/li&gt;
&lt;li&gt;차수(연결된 노드 수)의 두 규칙 중 한개를 만족해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 노드의 차수가 짝수이다.&lt;/li&gt;
&lt;li&gt;두 노드의 차수는 홀수, 나머지 노드는 짝수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건을 찾고 공부하느라 조금 늦어졌다ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 모든 정점이 연결되어있는지 찾기 위해 유니온 파인드를 사용했고, 차수 규칙의 경우 일반 구현으로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
input = sys.stdin.readline

def find(x):
    if parent[x] != x:
        parent[x] = find(parent[x])
    return parent[x]

def union(a, b):
    a = find(a)
    b = find(b)
    if a &amp;lt; b:
        parent[b] = a
    else:
        parent[a] = b

v, e = list(map(int, input().rstrip().split()))
graph = [[] for _ in range(v + 1)]
parent = [i for i in range(v + 1)]
for _ in range(e):
    a, b = list(map(int, input().rstrip().split()))
    if find(a) != find(b): union(a, b)
    graph[a].append(b)
    graph[b].append(a)


# 모두 연결되어있고, 차수 규칙에 맞아야함
temp = find(parent[1])
if not all(find(el) == temp for el in parent[1:]): # 연결되지 않은 경우
    print('NO')
    exit()
nodes = [len(node) % 2 for node in graph[1:]]
if all(el == 0 for el in nodes) or (nodes.count(1) == 2): # 차수 규칙
    print('YES')
else:
    print('NO')&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/문제풀이</category>
      <category>백준</category>
      <category>오일러경로</category>
      <category>유니온파인드</category>
      <category>코딩테스트</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/132</guid>
      <comments>https://99uulog.tistory.com/132#entry132comment</comments>
      <pubDate>Thu, 15 Feb 2024 17:36:45 +0900</pubDate>
    </item>
    <item>
      <title>백준 21314 민겸 수 Python</title>
      <link>https://99uulog.tistory.com/131</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bkm5Y/btsETSawjMs/B2FkXfZNoX00Si6kHiu5bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bkm5Y/btsETSawjMs/B2FkXfZNoX00Si6kHiu5bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bkm5Y/btsETSawjMs/B2FkXfZNoX00Si6kHiu5bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBkm5Y%2FbtsETSawjMs%2FB2FkXfZNoX00Si6kHiu5bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707979901588&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;21314번: 민겸 수&quot; data-og-description=&quot;민겸 수 하나가 주어진다. 민겸 수는 대문자 M과 K로만 이루어진 문자열이며, 길이는 3,000을 넘지 않는다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/21314&quot; data-og-url=&quot;https://www.acmicpc.net/problem/21314&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eAENGX/hyVmRedklL/F7X5qpxkDpmpsVaaQphJRK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/GSY5A/hyVm4SakGc/kbV7R0yU0lSDFQ6xHiw0KK/img.png?width=431&amp;amp;height=480&amp;amp;face=0_0_431_480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/21314&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/21314&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eAENGX/hyVmRedklL/F7X5qpxkDpmpsVaaQphJRK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480,https://scrap.kakaocdn.net/dn/GSY5A/hyVm4SakGc/kbV7R0yU0lSDFQ6xHiw0KK/img.png?width=431&amp;amp;height=480&amp;amp;face=0_0_431_480');&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;21314번: 민겸 수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;민겸 수 하나가 주어진다. 민겸 수는 대문자 M과 K로만 이루어진 문자열이며, 길이는 3,000을 넘지 않는다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;i&gt;&lt;b&gt;그리디, 구현&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;12.59m&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 직접 손으로 그려보면 그리디라는 것을 쉽게 파악할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 최대값과 최소값을 찾는 순서는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘다 기본적으로 주어진 입력에 대해 앞에서부터 순회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최대값&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;M이 나오면 stack에 담는다.&lt;/li&gt;
&lt;li&gt;K가 나오면 &lt;b&gt;K를 포함한 stack의 길이&lt;/b&gt;(n) 만큼 5 * (10 ^ (n - 1))를 문자열로 바꿔 정답 문자열에 더해준다.&lt;/li&gt;
&lt;li&gt;마지막에 stack에 담겨진 M을 모두 1로 치환하여 정답 문자열에 더해준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최소값&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;M이 나오면 stack에 담는다.&lt;/li&gt;
&lt;li&gt;K가 나오면 &lt;b&gt;현재까지 stack의 길이&lt;/b&gt;(n) 만큼 10 ^ (n - 1)를 문자열로 바꿔 정답 문자열에 더해준다.&lt;/li&gt;
&lt;li&gt;마지막에 stack에 담겨진 M의 수(n) 만큼 10 ^ (n - 1)을 문자열로 바꿔 정답 문자열에 더해준다.&lt;/li&gt;
&lt;/ol&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;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
input = sys.stdin.readline

nums = list(input().rstrip())

def minn():
    stack = []
    res = ''
    for num in nums:
        if num == 'K':
            if len(stack): res += str(10 ** (len(stack) - 1))
            res += '5'
            stack = []
        else:
            stack.append(num)
    if len(stack): res += str(10 ** (len(stack) - 1))
    return res

def maxx():
    stack = []
    res = ''
    for num in nums:
        if num == 'K':
            res += str(5 * (10 ** len(stack)))
            stack = []
        else:
            stack.append(num)
    for _ in stack:
        res += '1'
    return res

print(maxx())
print(minn())&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/문제풀이</category>
      <category>구현</category>
      <category>그리디</category>
      <category>백준</category>
      <category>코딩테스트</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/131</guid>
      <comments>https://99uulog.tistory.com/131#entry131comment</comments>
      <pubDate>Thu, 15 Feb 2024 15:56:52 +0900</pubDate>
    </item>
    <item>
      <title>백준 11279 최대 힙 Python</title>
      <link>https://99uulog.tistory.com/130</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ycoVb/btsEIPT0GCR/0WEPNMcQ6m1XVWDcjH4SLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ycoVb/btsEIPT0GCR/0WEPNMcQ6m1XVWDcjH4SLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ycoVb/btsEIPT0GCR/0WEPNMcQ6m1XVWDcjH4SLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FycoVb%2FbtsEIPT0GCR%2F0WEPNMcQ6m1XVWDcjH4SLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707892999359&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;11279번: 최대 힙&quot; data-og-description=&quot;첫째 줄에 연산의 개수 N(1 &amp;le; N &amp;le; 100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/11279&quot; data-og-url=&quot;https://www.acmicpc.net/problem/11279&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhVqIP/hyVje2Uu7T/GSyLbWA5e6cwO50FPOr7KK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11279&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/11279&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhVqIP/hyVje2Uu7T/GSyLbWA5e6cwO50FPOr7KK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;11279번: 최대 힙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 연산의 개수 N(1 &amp;le; N &amp;le; 100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;i&gt;&lt;b&gt;힙&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;06.24m&lt;/b&gt;&lt;/i&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;heapq를 이용하면 간단히 풀 수 있는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 heapq는 최소힙 구조이기 때문에, 음수를 붙여서 저장하고 출력할때 다시 음수를 붙여서 원래대로 바꾸는 형태로 구현했다.&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; &amp;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
import heapq
input = sys.stdin.readline

q = []
for _ in range(int(input().rstrip())):
    x = int(input().rstrip())
    if not x:
        if not q: print(0)
        else: print(-heapq.heappop(q))
    else:
        heapq.heappush(q, -x)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/문제풀이</category>
      <category>백준</category>
      <category>코딩테스트</category>
      <category>힙</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/130</guid>
      <comments>https://99uulog.tistory.com/130#entry130comment</comments>
      <pubDate>Wed, 14 Feb 2024 15:44:40 +0900</pubDate>
    </item>
    <item>
      <title>termterm : 가설 검증과 파일럿 프로젝트에 최적화된 마이크로 프론트엔드 환경 구축하기</title>
      <link>https://99uulog.tistory.com/129</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;top_banner.png&quot; data-origin-width=&quot;6530&quot; data-origin-height=&quot;3218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qs8pw/btsEP7MaPrM/hKqV9NKZbmKjgqd6QdzhKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qs8pw/btsEP7MaPrM/hKqV9NKZbmKjgqd6QdzhKK/img.png&quot; data-alt=&quot;pnpm workspace 기반의 모노레포 가이드라인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qs8pw/btsEP7MaPrM/hKqV9NKZbmKjgqd6QdzhKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqs8pw%2FbtsEP7MaPrM%2FhKqV9NKZbmKjgqd6QdzhKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6530&quot; height=&quot;3218&quot; data-filename=&quot;top_banner.png&quot; data-origin-width=&quot;6530&quot; data-origin-height=&quot;3218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pnpm workspace 기반의 모노레포 가이드라인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 갑자기 리팩토링?&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707834218403&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;&amp;lrm;termterm&quot; data-og-description=&quot;&amp;lrm;termterm은 IT 산업 전반에서 사용되는 용어를 정리하고 공유하며 학습할 수 있도록 하는 서비스예요. 처음 사회생활을 시작할 때 또는 프로젝트를 하며 우리는 각자의 전문 분야를 가진 다양한 &quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot; data-og-url=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cFt7V6/hyVjg7iTby/ZeWhaOrmWzbwyPXvV2Dce1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bNFXcA/hyVjgztqYN/R3NhpBc1IHaofJv22SG0d1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cFt7V6/hyVjg7iTby/ZeWhaOrmWzbwyPXvV2Dce1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bNFXcA/hyVjgztqYN/R3NhpBc1IHaofJv22SG0d1/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;&amp;lrm;termterm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lrm;termterm은 IT 산업 전반에서 사용되는 용어를 정리하고 공유하며 학습할 수 있도록 하는 서비스예요. 처음 사회생활을 시작할 때 또는 프로젝트를 하며 우리는 각자의 전문 분야를 가진 다양한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.apple.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;기존에 expo-go 기반으로 개발한 &lt;a href=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;termterm&lt;/a&gt;의 규모가 커지면서, 가설 검증의 플로우를 빠르게 하기 위한 새로운 OKR로 &quot;&lt;b&gt;웹뷰 기반의 하이브리드 앱으로 리팩토링하기&quot;&lt;/b&gt;를 설정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 팀인 termterm 팀에서는 환경의 문제로 인해 적용하지 못했던 각종 문제(푸시, 결제, GA 등)를 해결하고 파일럿 프로젝트에 빠르게 도전하여 실 사용자의 반응을 확인하기 위한 수단이 필요한 상황이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 오로지 React-Native 코드로만 관리하던 서비스를 최소 단위의 &lt;b&gt;micro-frontend&lt;/b&gt;로 분리하고, 핵심 기능과 컨트롤러만 React Native CLI로 관리하자는 계획을 세우게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;본 시리즈의 목표는&lt;span&gt;&amp;nbsp;&lt;b&gt;한 개의&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;모노레포 &lt;/b&gt;속에서 다양한 JS 기반 서비스를 배포할 수 있는 worktree를 구축하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유틸함수와 hook, ui를 분리하여, 최종적으로 모바일 앱 뿐 아니라, 웹, 익스텐션, 데스크탑 앱까지 개발할 수 있는 환경을 구축하고자 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707834432330&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;root
├── common //공통 모듈
│   ├── libs //유틸
│   ├── hooks //react hook
│   ├── ui //pure ui
│   └── shared //scss token
└── packages
    └── ... //각종 프로젝트들&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이 본문이 도움이 될거예요&lt;/h3&gt;
&lt;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;pnpm을 이용한 모노레포 환경 구축&lt;/li&gt;
&lt;li&gt;typescript, rollup.js를 이용한 유틸 함수 모듈 구축&lt;/li&gt;
&lt;li&gt;react, typescript, rollup.js를 이용한 React hook 모듈 구축&lt;/li&gt;
&lt;li&gt;react, typescript, rollup.js, scss(saas), storybook을 이용한 React UI 모듈 구축&lt;/li&gt;
&lt;li&gt;모노레포에서 워크스페이스 간 모듈 참조&lt;/li&gt;
&lt;li&gt;기본적인 rollup.config.js 작성법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;pnpm?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 yarn berry와 lerna, turborepo까지 시도해보며, termterm에서는 pnpm workspace 아키텍쳐가 가장 적합할 것이라 판단하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn berry의 경우 pnp가 가장 강력한 기능인데, 모노레포 도입 과정에서 pnp를 포기하게 되는 상황이 생각보다 자주 발생한다는 것을 알 수 있었습니다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1707834949720&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;프론트엔드 프로젝트 최신화 - 1편 : Yarn berry&quot; data-og-description=&quot;안녕하세요. 채널톡 웹팀 엔지니어 카를로스입니다   채널톡에서 진행하고 있는 프로젝트 최신화에 관해 소개해드리려 합니다. 들어가며 채널톡 웹팀에서는 많은 일들을 하고 있어요. 그래서&quot; data-og-host=&quot;channel.io&quot; data-og-source-url=&quot;https://channel.io/ko/blog/frontend_yarnberry&quot; data-og-url=&quot;https://channel.io/ko/blog/frontend_yarnberry&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pmXcN/hyVjeuTgAD/kDy0eltPOwTJ982wDzHnX1/img.png?width=1431&amp;amp;height=837&amp;amp;face=0_0_1431_837,https://scrap.kakaocdn.net/dn/st5LY/hyVjhd3KCg/kKFGdG0WJ85kfOtzl3AaiK/img.png?width=671&amp;amp;height=430&amp;amp;face=0_0_671_430&quot;&gt;&lt;a href=&quot;https://channel.io/ko/blog/frontend_yarnberry&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://channel.io/ko/blog/frontend_yarnberry&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pmXcN/hyVjeuTgAD/kDy0eltPOwTJ982wDzHnX1/img.png?width=1431&amp;amp;height=837&amp;amp;face=0_0_1431_837,https://scrap.kakaocdn.net/dn/st5LY/hyVjhd3KCg/kKFGdG0WJ85kfOtzl3AaiK/img.png?width=671&amp;amp;height=430&amp;amp;face=0_0_671_430');&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;프론트엔드 프로젝트 최신화 - 1편 : Yarn berry&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. 채널톡 웹팀 엔지니어 카를로스입니다   채널톡에서 진행하고 있는 프로젝트 최신화에 관해 소개해드리려 합니다. 들어가며 채널톡 웹팀에서는 많은 일들을 하고 있어요. 그래서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;channel.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 채널톡의 기술 블로그 인상깊게 읽었는데, pnp를 포기하게 된 이유를 보고... 어쨌든 규모가 커져서 분리하는 것이 아닌 가설 검증을 위해 분리하는 termterm에서 굳이 yarn berry를 적용할 필요가 없다 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;당장 node_modules를 이용하는 react native와 발생하는 심볼릭 링크 관련 충돌을 직접 겪으며 yarn berry 포기를 확신했습니다.&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;결국 pnp가 아니면서, workspace 기능이 훌륭한 pnpm이 채택되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(lerna와 turborepo, nx의 경우 프레임워크의 성격이 짙다 느껴, 규모가 크지 않은 termterm에서는 배제하게 되었습니다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;모노레포 환경 구축&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉토리를 생성하고 경로 이동 후 아래 명령어를 실행하여 퓨어한 &lt;b&gt;package.json&lt;/b&gt;을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1705504361042&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 정보는 자유롭게 설정하되, 직접 수정해야하는 부분이 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1705504471801&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;termterm-monorepo&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;termterm hybrid web monorepo&quot;,
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모노레포에서 이용할 워크스페이스를 구분하기 위해 우선 &lt;b&gt;pnpm-workspace.yaml&lt;/b&gt; 파일을 생성하여, 워크스페이스를 등록합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707835866855&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;packages:
  - &quot;common/*&quot;
  - &quot;packages/*&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;termterm의 경우 common의 하위와 packages 하위에 여러 모듈 &amp;amp; 프로젝트가 위치되기 때문에 위와 같이 설정했습니다.&lt;/p&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;각 디렉토리 하위에서도 pnpm init을 통해 기본 package.json을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;워크스페이스 별 name을 통해 루트에서 해당 모듈에 편하게 접근할 수 있도록 설정&lt;/b&gt;할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령 common/libs의 경우 package.json을 아래와 같이 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707836109324&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@termterm/libs&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;루트의 package.json에서 아래와 같이 스크립트를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707836177927&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
  &quot;scripts&quot;: {
    &quot;libs&quot;: &quot;pnpm -F @termterm/libs&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;루트에서 각 워크스페이스에 접근하여 cli를 입력하기 위해서는 &lt;i&gt;pnpm -F 워크스페이스 이름 + cli&lt;/i&gt; 까지 입력해야해서 상당히 번거롭습니다. 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;때문에 위와 같이 &lt;b&gt;해당 워크스페이스에 대한 접근 명령어를 스크립트&lt;/b&gt;로 작성해둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 루트에서 pnpm libs + cli 를 통해 @termterm/libs 워크스페이스에서의 작업을 모두 진행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;공통 typescript, eslint 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 선택할 수 있지만, 본 프로젝트에서는 모든 워크스페이스에서 typescript가 사용되기도 하고 (공통으로 설정해야할 속성도 있고), 저 혼자 개발하는 것이 아니기 때문에 공용으로 사용되는 eslint를 설정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eslint의 경우 상위 경로에서 설정한 것이 더 높은 우선순위를 갖기 때문에, 루트에서 설정하면 하위 워크스페이스에서는 설정할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707836594876&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm add -w -D typescript
pnpm add -w -D eslint eslint-config-airbnb-typescript eslint-config-airbnb&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;eslint는 airbnb의 설정을 가져와 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707836629376&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tsc --init&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;이후 생성되는 tsconfig.js의 이름을 &lt;b&gt;tsconfig.base.js&lt;/b&gt;로 변경하여 공용이라는 것을 명시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일 내부에 다른 속성은 크게 상관 없고, &lt;b&gt;preserveSymlinks만 true&lt;/b&gt;로 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 pnpm에서 공용 모듈 간 자동 import 문제를 해결하기 위함으로, 공용 모듈을 사용하는 워크스페이스에서 해당 속성이 false라면 자동 import가 되지 않습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1707836839238&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;VSCode auto complete/import not working &amp;middot; Issue #3671 &amp;middot; pnpm/pnpm&quot; data-og-description=&quot;pnpm version: 6.13.0 Code to reproduce the issue: import React from 'react' export default function App() { useEffect(() =&amp;gt; { console.log('Intellisense to import useEffect not working') }, []) retu...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pnpm/pnpm/issues/3671&quot; data-og-url=&quot;https://github.com/pnpm/pnpm/issues/3671&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g4LZw/hyVi9f16Fu/2CHNZzUY9oUEl0QD2uSkck/img.png?width=1200&amp;amp;height=600&amp;amp;face=1003_123_1053_177&quot;&gt;&lt;a href=&quot;https://github.com/pnpm/pnpm/issues/3671&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pnpm/pnpm/issues/3671&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g4LZw/hyVi9f16Fu/2CHNZzUY9oUEl0QD2uSkck/img.png?width=1200&amp;amp;height=600&amp;amp;face=1003_123_1053_177');&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;VSCode auto complete/import not working &amp;middot; Issue #3671 &amp;middot; pnpm/pnpm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;pnpm version: 6.13.0 Code to reproduce the issue: import React from 'react' export default function App() { useEffect(() =&amp;gt; { console.log('Intellisense to import useEffect not working') }, []) retu...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트에 .eslintrc.js를 생성하고 아래와 같이 입력하여 airbnb의 세팅을 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707836941509&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  parser: &quot;@typescript-eslint/parser&quot;,
  parserOptions: {
    project: &quot;./tsconfig.json&quot;,
    tsconfigRootDir: __dirname,
    sourceType: &quot;module&quot;,
  },
  plugins: [&quot;@typescript-eslint/eslint-plugin&quot;],
  extends: [
    &quot;airbnb-base&quot;,
    &quot;airbnb-typescript/base&quot;,
    &quot;plugin:@typescript-eslint/recommended&quot;,
    &quot;plugin:prettier/recommended&quot;,
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: [&quot;.eslintrc.js&quot;],
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;공용 유틸 함수 모듈 환경 구축&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;typescript로 유틸함수를 작성하고, rollup.js로 번들링합니다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707837090558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm libs add -D typescript tslib rollup @rollup/plugin-typescript&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;루트에서 위 명령어를 입력하여 @termterm/libs 워크스페이스에 필요한 모듈을 설치했습니다.&lt;/p&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;rollup.config.js&lt;/b&gt;를 생성해서 번들링 옵션을 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837170557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const typescript = require('@rollup/plugin-typescript')
const fs = require('fs')
const path = require('path')

const options = {
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/index.js',
      format: 'cjs'
    },
    {
      file: 'dist/index.es.js',
      format: 'es'
    }
  ],
  plugins: [
    {
      name: 'Erase Dist',
      buildStart() {
        fs.rmSync(path.resolve('dist'), { recursive: true, force: true })
      }
    },
    typescript({
      module: 'esnext',
      declaration: true,
      declarationDir: './dist'
    })
  ]
}

module.exports = options&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;index.ts에서 내보낸 함수를 dist 디렉토리 하위에 commonjs와 es module 형태로 내보냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 직전에 빌드한 기록을 지우기 위해 파일 시스템의 rmSync를 이용해 dist를 한 번 지워냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rollup 설정이 완료되었다면, 빌드 스크립트 작성을 위해 &lt;b&gt;package.json&lt;/b&gt;을 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837486176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@termterm/libs&quot;,
  &quot;main&quot;: &quot;dist/index.js&quot;,
  &quot;module&quot;: &quot;dist/index.es.js&quot;,
  &quot;declaration&quot;: &quot;dist/index.d.ts&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;rollup -c rollup.config.js -w&quot;,
    &quot;build&quot;: &quot;rollup -c rollup.config.js&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@rollup/plugin-typescript&quot;: &quot;^11.1.6&quot;,
    &quot;rollup&quot;: &quot;^4.9.6&quot;,
    &quot;tslib&quot;: &quot;^2.6.2&quot;,
    &quot;typescript&quot;: &quot;^5.3.3&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tsconfig.js&lt;/b&gt; 설정은 아래와 같이합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837316985&quot; class=&quot;json&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es2016&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;esModuleInterop&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;strict&quot;: false,
    &quot;skipLibCheck&quot;: true
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;번들&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-14 오전 12.15.57.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GSr8Y/btsEGLqnOV4/okgFHSkOmXZeAUgjQjruu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GSr8Y/btsEGLqnOV4/okgFHSkOmXZeAUgjQjruu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GSr8Y/btsEGLqnOV4/okgFHSkOmXZeAUgjQjruu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGSr8Y%2FbtsEGLqnOV4%2FokgFHSkOmXZeAUgjQjruu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;151&quot; data-filename=&quot;스크린샷 2024-02-14 오전 12.15.57.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 유틸함수를 작성하여 &lt;b&gt;src/index.ts&lt;/b&gt;로 가져옵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837425805&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export { default as add } from &quot;./calculate/add&quot;;
export { default as minus } from &quot;./calculate/minus&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;이후 pnpm build로 작성한 유틸함수를 번들링합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;번들링된 모듈을 같은 모노레포의 다른 워크스페이스에서 참고하는 방법은 포스팅 최 하단에서 확인할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;공용 React Hook 모듈 환경 구축&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;typescript와 React로 Custom Hook을 작성하고, rollup.js로 번들링합니다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707837699246&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;pnpm hooks add react react-dom
pnpm hooks add -D @babel/core @babel/preset-env @babel/preset-react
pnpm hooks add -D @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript
pnpm hooks add -D @types/react rollup rollup-plugin-peer-deps-external tslib typescript&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;루트에서 위 명령어를 입력하여 @termterm/hooks 워크스페이스에 필요한 모듈을 설치했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;rollup.config.js&lt;/b&gt;를 생성해서 번들링 옵션을 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837881181&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const typescript = require('@rollup/plugin-typescript')
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')
const external = require('rollup-plugin-peer-deps-external')
const babel = require('@rollup/plugin-babel').default
const fs = require('fs')
const path = require('path')

const options = {
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/index.js',
      format: 'cjs'
    },
    {
      file: 'dist/index.es.js',
      format: 'es'
    }
  ],
  plugins: [
    external(),
    {
      name: 'Erase Dist',
      buildStart() {
        fs.rmSync(path.resolve('dist'), { recursive: true, force: true })
      }
    },
    nodeResolve(),
    commonjs(),
    babel({ babelHelpers: 'bundled' }),
    typescript({
      module: 'esnext',
      declaration: true,
      declarationDir: './dist'
    })
  ],
  external: ['react', 'react-dom']
}

module.exports = options&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 유틸 함수보다는 조금 길어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;rollup 설정이 완료되었다면, 마찬가지로 빌드 스크립트 작성을 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;package.json&lt;/b&gt;을 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707837973154&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@termterm/hooks&quot;,
  &quot;main&quot;: &quot;dist/index.js&quot;,
  &quot;module&quot;: &quot;dist/index.es.js&quot;,
  &quot;declaration&quot;: &quot;dist/index.d.ts&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;rollup -c rollup.config.js -w&quot;,
    &quot;build&quot;: &quot;rollup -c rollup.config.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^18.2.0&quot;,
    &quot;react-dom&quot;: &quot;^18.2.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@babel/core&quot;: &quot;^7.23.9&quot;,
    &quot;@babel/preset-env&quot;: &quot;^7.23.9&quot;,
    &quot;@babel/preset-react&quot;: &quot;^7.23.3&quot;,
    &quot;@rollup/plugin-babel&quot;: &quot;^6.0.4&quot;,
    &quot;@rollup/plugin-commonjs&quot;: &quot;^25.0.7&quot;,
    &quot;@rollup/plugin-node-resolve&quot;: &quot;^15.2.3&quot;,
    &quot;@rollup/plugin-typescript&quot;: &quot;^11.1.6&quot;,
    &quot;@types/react&quot;: &quot;^18.2.55&quot;,
    &quot;rollup&quot;: &quot;^4.9.6&quot;,
    &quot;rollup-plugin-peer-deps-external&quot;: &quot;^2.2.4&quot;,
    &quot;tslib&quot;: &quot;^2.6.2&quot;,
    &quot;typescript&quot;: &quot;^5.3.3&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tsconfig.js&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정은 유틸함수 워크스페이스와 동일합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;번들&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-14 오전 12.27.30.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBaTzA/btsEQPYJlED/3XUPte7MWlDHjr9kfKtfnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBaTzA/btsEQPYJlED/3XUPte7MWlDHjr9kfKtfnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBaTzA/btsEQPYJlED/3XUPte7MWlDHjr9kfKtfnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBaTzA%2FbtsEQPYJlED%2F3XUPte7MWlDHjr9kfKtfnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;125&quot; data-filename=&quot;스크린샷 2024-02-14 오전 12.27.30.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;226&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;src/index.ts&lt;/b&gt;로 가져와 내보냅니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;테스트를 위해 임시로 작성한 커스텀 훅입니다.&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707838133590&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { useState } from &quot;react&quot;;

const useCount = () =&amp;gt; {
  const [count, setCount] = useState(0);

  const increment = () =&amp;gt; setCount(count + 1);
  const decrement = () =&amp;gt; setCount(count - 1);

  return { count, increment, decrement };
};

export default useCount;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707838145036&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { ChangeEvent } from &quot;react&quot;;
import { useState } from &quot;react&quot;;

const useInput = () =&amp;gt; {
  const [value, setValue] = useState(&quot;&quot;);

  const handleValue = (e: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    setValue(e.target.value);
  };

  return { value, handleValue };
};

export default useInput;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1707838188107&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export { default as useCounter } from &quot;./input/useCounter&quot;;
export { default as useInput } from &quot;./input/useInput&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;이후 pnpm build로 작성한 커스텀 훅을 번들링합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;공용 UI 모듈 환경 구축&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;typescript와 React, SCSS로 공통 디자인 시스템을 작성하고, Storybook(webpack5)으로 UI 테스트 후 rollup.js로 번들링합니다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707887732550&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm ui install react react-dom style-inject classnames
pnpm ui install -D css-loader postcss rollup rollup-plugin-peer-deps-external rollup-plugin-postcss sass sass-loader style-loader tslib typescript
pnpm ui install -D @babel/core @babel/preset-env @babel/preset-react
pnpm ui install -D @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript
pnpm ui install -D @types/react&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;루트에서 위 명령어로 typescript, react, rollup 개발 환경을 구성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;storybook은 storybookjs에서 지원하는 템플릿으로 빠르게 구성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707888014894&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;cd common/ui
npx sb init //명령어 입력 후, webpack5 선택&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;storybook에서 scss를 사용하기 위해서는 추가적인 모듈이 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707888093937&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//경로가 루트라면 -F 필요함
pnpm add -D @storybook/preset-scss&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;이후, typescript와 rollup, babel, storybook에 대한 추가 설정 작업이 필요합니다.&lt;/p&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;tsconfig.js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707888242987&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es5&quot;,
    &quot;esModuleInterop&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;strict&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;jsx&quot;: &quot;react&quot;,
    &quot;module&quot;: &quot;ESNext&quot;,
    &quot;sourceMap&quot;: false,
    &quot;outDir&quot;: &quot;./dist&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;allowSyntheticDefaultImports&quot;: true,
    &quot;removeComments&quot;: true
  },
  &quot;exclude&quot;: [
    &quot;./dist&quot;,
    &quot;./node_modules&quot;,
    &quot;./src/**/*.test.tsx&quot;,
    &quot;./src/**/*.stories.tsx&quot;,
    &quot;./src/**/*.test.ts&quot;,
    &quot;./src/**/*.stories.ts&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;rollup.config.js&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707888265980&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import babel from &quot;@rollup/plugin-babel&quot;;
import commonjs from &quot;@rollup/plugin-commonjs&quot;;
import { nodeResolve } from &quot;@rollup/plugin-node-resolve&quot;;
import typescript from &quot;@rollup/plugin-typescript&quot;;
import peerDepsExternal from &quot;rollup-plugin-peer-deps-external&quot;;
import postcss from &quot;rollup-plugin-postcss&quot;;
import pkg from &quot;./package.json&quot; assert { type: &quot;json&quot; };
import { rmSync } from &quot;fs&quot;;
import { resolve } from &quot;path&quot;;

const extensions = [&quot;js&quot;, &quot;jsx&quot;, &quot;ts&quot;, &quot;tsx&quot;, &quot;mjs&quot;];

const config = [
  {
    external: [/node_modules/],
    input: &quot;src/index.ts&quot;,
    output: [
      {
        dir: &quot;dist&quot;,
        format: &quot;cjs&quot;,
        preserveModules: true,
        preserveModulesRoot: &quot;src&quot;,
      },
      {
        file: pkg.module,
        format: &quot;es&quot;,
      },
      {
        name: pkg.name,
        file: pkg.browser,
        format: &quot;umd&quot;,
      },
    ],
    plugins: [
      {
        name: &quot;Erase Dist&quot;,
        buildStart() {
          rmSync(resolve(&quot;dist&quot;), { recursive: true, force: true });
        },
      },
      nodeResolve({ extensions }),
      babel({
        exclude: &quot;node_modules/**&quot;,
        extensions,
        include: [&quot;src/**/*&quot;],
      }),
      commonjs({ include: &quot;node_modules/**&quot; }),
      peerDepsExternal(),
      typescript({
        module: &quot;esnext&quot;,
        declaration: true,
        declarationDir: &quot;./dist&quot;,
      }),
      postcss({
        extract: false,
        inject: (cssVariableName) =&amp;gt;
          `import styleInject from 'style-inject';\nstyleInject(${cssVariableName});`,
        modules: false,
        sourceMap: false,
        use: [&quot;sass&quot;],
      }),
    ],
  },
];

export default config;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.babelrc&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707888325502&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;presets&quot;: [&quot;@babel/preset-env&quot;, &quot;@babel/preset-react&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.stroybook/main.ts&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707888427934&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { StorybookConfig } from &quot;@storybook/react-webpack5&quot;;

import { join, dirname } from &quot;path&quot;;

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value: string): any {
  return dirname(require.resolve(join(value, &quot;package.json&quot;)));
}
const config: StorybookConfig = {
  stories: [&quot;../src/**/*.mdx&quot;, &quot;../src/**/*.stories.@(js|jsx|mjs|ts|tsx)&quot;], //경로 변경
  addons: [
    getAbsolutePath(&quot;@storybook/addon-links&quot;),
    getAbsolutePath(&quot;@storybook/addon-essentials&quot;),
    getAbsolutePath(&quot;@storybook/addon-onboarding&quot;),
    getAbsolutePath(&quot;@storybook/addon-interactions&quot;),
    getAbsolutePath(&quot;@storybook/preset-scss&quot;), //추가
  ],
  framework: {
    name: getAbsolutePath(&quot;@storybook/react-webpack5&quot;),
    options: {
      builder: {
        useSWC: true,
      },
    },
  },
  docs: {
    autodocs: &quot;tag&quot;,
  },
};
export default config;&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;경로를 src 하위의 index.ts로 바꾸었으므로, 아래와 같이 아키텍처를 구성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-14 오후 2.28.15.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEGstF/btsERmo4na7/K83ts0JyRUQtlHdVZqoNEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEGstF/btsERmo4na7/K83ts0JyRUQtlHdVZqoNEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEGstF/btsERmo4na7/K83ts0JyRUQtlHdVZqoNEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEGstF%2FbtsERmo4na7%2FK83ts0JyRUQtlHdVZqoNEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;185&quot; data-filename=&quot;스크린샷 2024-02-14 오후 2.28.15.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지의 Button은 테스트를 위해 임시로 제작한 컴포넌트로, src 하위의 아키텍처는 자유롭게 구성해도됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작한 컴포넌트는 아래와 같이 index.ts에서 내보내야 번들링이 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707888598071&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export { default as Button } from &quot;./components/Button&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 제작한 디자인 시스템은, &lt;i&gt;&lt;b&gt;pnpm storybook&lt;/b&gt;&lt;/i&gt;으로 테스트하고 &lt;i&gt;&lt;b&gt;pnpm build&lt;/b&gt;&lt;/i&gt;를 통해 번들링할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;common 모듈 공유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방식으로 세팅한 모든 공통 모듈을 packages 하위의 프로젝트에서 공유하여 사용할 수 있어야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 packages 하위에 모듈을 테스트할 &lt;b&gt;prototype&amp;nbsp;&lt;/b&gt;프로젝트를 생성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 프로젝트의 경우, 원하는 스택으로 생성하면 됩니다. 본 포스팅에서는 vite와 react typescript 조합의 템플릿을 이용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707888842385&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd pacakges
pnpm create vite prototype&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;마찬가지로, package.json에서 프로젝트의 이름을 적절하게 수정하고 루트에 -F 과정을 생략할 스크립트를 추가합니다.&lt;/p&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;prototype/pacakge.json&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707888972956&quot; class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@termterm/prototype&quot;,
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;root/package.json&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707889021876&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;termterm-monorepo&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;termterm hybrid web monorepo&quot;,
  &quot;scripts&quot;: {
    &quot;libs&quot;: &quot;pnpm -F @termterm/libs&quot;,
    &quot;hooks&quot;: &quot;pnpm -F @termterm/hooks&quot;,
    &quot;ui&quot;: &quot;pnpm -F @termterm/ui&quot;,
    &quot;prototype&quot;: &quot;pnpm -F @termterm/prototype&quot; // 추가
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;devDependencies&quot;: {
    &quot;eslint&quot;: &quot;^8.56.0&quot;,
    &quot;eslint-config-airbnb&quot;: &quot;^19.0.4&quot;,
    &quot;eslint-config-airbnb-typescript&quot;: &quot;^17.1.0&quot;,
    &quot;typescript&quot;: &quot;^5.3.3&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;이후 prototype 프로젝트에 공통 모듈을 설치하여 테스트를 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707889098183&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm prototype add @termterm/libs @termterm/hooks @termterm/ui&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 cli는, &lt;b&gt;@termterm/prototype에 @termterm/libs @termterm/hooks @termterm/ui를 설치&lt;/b&gt;하겠다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 pacakge.json에 아래와 같은 형식으로 모듈이 추가됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707889233132&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  ...
  &quot;dependencies&quot;: {
    &quot;@termterm/hooks&quot;: &quot;workspace:^&quot;,
    &quot;@termterm/libs&quot;: &quot;workspace:^&quot;,
    &quot;@termterm/ui&quot;: &quot;workspace:^&quot;,
    ..
  },
  ...
}&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;스크린샷 2024-02-14 오후 2.40.51.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w3wLR/btsEORXIJHZ/DVzdkAPE9J8kEMUpFHdaQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w3wLR/btsEORXIJHZ/DVzdkAPE9J8kEMUpFHdaQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w3wLR/btsEORXIJHZ/DVzdkAPE9J8kEMUpFHdaQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw3wLR%2FbtsEORXIJHZ%2FDVzdkAPE9J8kEMUpFHdaQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;217&quot; data-filename=&quot;스크린샷 2024-02-14 오후 2.40.51.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;370&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;node_modules에도 공통모듈이 올바르게 설치되었다는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료된 모듈은 이제 자유롭게 import하여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 자동으로 import되지 않는 문제가 발생한다면, 프로젝트의 tsconfig.js를 아래와 같이 수정하여 &lt;b&gt;루트의 tsconfig를 상속&lt;/b&gt;받도록 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707889361148&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2020&quot;,
    &quot;useDefineForClassFields&quot;: true,
    &quot;lib&quot;: [&quot;ES2020&quot;, &quot;DOM&quot;, &quot;DOM.Iterable&quot;],
    &quot;module&quot;: &quot;ESNext&quot;,

    /* Bundler mode */
    &quot;moduleResolution&quot;: &quot;bundler&quot;,
    &quot;allowImportingTsExtensions&quot;: true,
    &quot;resolveJsonModule&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react-jsx&quot;
  },
  &quot;include&quot;: [&quot;src&quot;],
  &quot;extends&quot;: &quot;./../../tsconfig.base.json&quot;, //추가
  &quot;references&quot;: [{ &quot;path&quot;: &quot;./tsconfig.node.json&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;루트의 tsconfig.base.json을 설정할 때 봤던 &lt;b&gt;preserveSymlinks&lt;/b&gt;를 상속받아 해당 문제를 해결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(번외) 공통 스타일 토큰 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scss의 강점이라고 하면, 빼놓을 수 없는 것이 바로 mixin과 변수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로젝트에서 하나의 스타일 토큰을 import하여 사용하기 위해 common 하위에 shared 디렉토리를 구성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 설정은 필요하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;common/shared/var.scss&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707889683317&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

$temp1: aqua;
$temp2: crimson;&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;이후 각 프로젝트의 scss 최상단에서 해당 토큰을 import합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;vite의 경우 config를 이용해서 해당 과정을 자동화할 수 있습니다. 이 과정은 다음 글에서 보여드리겠습니다.&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707889935989&quot; class=&quot;css&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;@import &quot;../../../common/shared/var.scss&quot;;

.temp-1 {
  background-color: $temp1;
  color: $temp2;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하나의 IP를 공유하는 서비스 내부에서, 다양한 가설을 세우고 도입하며 사용자의 반응을 검증할 수 있는 환경 세팅이 마무리 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모노레포를 자주 사용했음에도, RN과 같은 툴이 끼면서 꽤 오랜 시간 고민하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 PS만하면서 개발과 조금 멀어졌다는 느낌이 있었는데, 확실히 성장이 멈췄다고 느낄 때에는 도전만큼 좋은게 없는 것 같습니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://termterm-official.notion.site/termterm-c18426829f164f729940ceae408d450a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;  텀텀(termterm) 소개 페이지&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/termterm/id6467411513&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;  텀텀(termterm) 앱스토어 다운로드&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://walla.my/termterm.android.beta&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;  텀텀(termterm) 구글 플레이스토어 베타 테스터 신청&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://brunch.co.kr/@ihyemin39/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;  텀텀(termterm) 런칭기&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/frontend</category>
      <category>MSA</category>
      <category>pnpm</category>
      <category>react</category>
      <category>rollup</category>
      <category>termterm</category>
      <category>typescript</category>
      <category>마이크로프론트엔드</category>
      <category>모노레포</category>
      <category>텀텀</category>
      <category>프론트엔드</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/129</guid>
      <comments>https://99uulog.tistory.com/129#entry129comment</comments>
      <pubDate>Wed, 14 Feb 2024 15:04:56 +0900</pubDate>
    </item>
    <item>
      <title>백준 1027 고층 건물 Python</title>
      <link>https://99uulog.tistory.com/128</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqH0za/btsEHp7vFTW/NdzBM0eGNynvusDNlhtq21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqH0za/btsEHp7vFTW/NdzBM0eGNynvusDNlhtq21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqH0za/btsEHp7vFTW/NdzBM0eGNynvusDNlhtq21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqH0za%2FbtsEHp7vFTW%2FNdzBM0eGNynvusDNlhtq21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707727923554&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;1027번: 고층 건물&quot; data-og-description=&quot;세준시에는 고층 빌딩이 많다. 세준시의 서민 김지민은 가장 많은 고층 빌딩이 보이는 고층 빌딩을 찾으려고 한다. 빌딩은 총 N개가 있는데, 빌딩은 선분으로 나타낸다. i번째 빌딩 (1부터 시작)&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1027&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1027&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwyuKt/hyVjhEEQk7/2MjXXBFY4oYnkfBK1fyuE1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1027&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1027&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwyuKt/hyVjhEEQk7/2MjXXBFY4oYnkfBK1fyuE1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1027번: 고층 건물&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;세준시에는 고층 빌딩이 많다. 세준시의 서민 김지민은 가장 많은 고층 빌딩이 보이는 고층 빌딩을 찾으려고 한다. 빌딩은 총 N개가 있는데, 빌딩은 선분으로 나타낸다. i번째 빌딩 (1부터 시작)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;i&gt;&lt;b&gt;수학&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;15.12m&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 스택 문제라고 생각했는데, &lt;b&gt;기울기를 비교해야하는 수학&lt;/b&gt; 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 스택보다 조건 자체는 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽으로 이동하면서 이전까지 나왔던 최대 기울기보다 큰 기울기가 나와야만 1개를 추가해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A와 B 빌딩 서로 볼 수 있기 때문에 두 요소 모두 1을 추가해야한다.&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;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
input = sys.stdin.readline

n = int(input().rstrip())
nums = list(map(int, input().rstrip().split()))
res = [0] * n

for i in range(n - 1):
    temp = -sys.maxsize
    for j in range(i + 1, n):
        if temp &amp;lt; (nums[j] - nums[i]) / (j - i):
            res[i] += 1
            res[j] += 1
            temp = (nums[j] - nums[i]) / (j - i)

print(max(res))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/문제풀이</category>
      <category>백준</category>
      <category>수학</category>
      <category>코딩테스트</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/128</guid>
      <comments>https://99uulog.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 12 Feb 2024 17:54:00 +0900</pubDate>
    </item>
    <item>
      <title>백준 1342 행운의 문자열 Python</title>
      <link>https://99uulog.tistory.com/127</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yxDJM/btsEEVT4q3j/ur5NtxKr86ChEabaOpu94K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yxDJM/btsEEVT4q3j/ur5NtxKr86ChEabaOpu94K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yxDJM/btsEEVT4q3j/ur5NtxKr86ChEabaOpu94K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyxDJM%2FbtsEEVT4q3j%2Fur5NtxKr86ChEabaOpu94K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707725658514&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;1342번: 행운의 문자열&quot; data-og-description=&quot;민식이와 준영이는 자기 방에서 문자열을 공부하고 있다. 민식이가 말하길 인접해 있는 모든 문자가 같지 않은 문자열을 행운의 문자열이라고 한다고 한다. 준영이는 문자열 S를 분석하기 시작&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1342&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1342&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYIjeQ/hyVjcwAVwf/6Bt2L1ABDyU5R9OeF68jU0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1342&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYIjeQ/hyVjcwAVwf/6Bt2L1ABDyU5R9OeF68jU0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1342번: 행운의 문자열&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;민식이와 준영이는 자기 방에서 문자열을 공부하고 있다. 민식이가 말하길 인접해 있는 모든 문자가 같지 않은 문자열을 행운의 문자열이라고 한다고 한다. 준영이는 문자열 S를 분석하기 시작&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;i&gt;&lt;b&gt;백트래킹&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;14.41m&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 순열로 풀어야하는 문제라고 생각해서... 도대체 어떻게 풀어야하지?! 고민했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 천천히 생각해보니 그냥 Counter 써서 뒤에 뭐올지 백트래킹해도 될 것 같다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 문자열 길이가 10까지밖에 안돼서...&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;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
from collections import Counter
input = sys.stdin.readline

word = list(input().rstrip())
length = len(word)
letters = Counter(word)
res = set()

def bt(prev, cur):
    if len(cur) == length:
        res.add(cur)
        return
    for letter in letters:
        if (prev == letter) or (letters[letter] == 0): continue
        letters[letter] -= 1
        bt(letter, cur + letter)
        letters[letter] += 1

bt('-', '')

print(len(res))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/문제풀이</category>
      <category>백준</category>
      <category>백트래킹</category>
      <category>코딩테스트</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/127</guid>
      <comments>https://99uulog.tistory.com/127#entry127comment</comments>
      <pubDate>Mon, 12 Feb 2024 17:15:43 +0900</pubDate>
    </item>
    <item>
      <title>백준 17141 연구소 2 Python</title>
      <link>https://99uulog.tistory.com/126</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBHv29/btsEFOz9sOm/U1qVKTHZKVkuZOi7njyif1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBHv29/btsEFOz9sOm/U1qVKTHZKVkuZOi7njyif1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBHv29/btsEFOz9sOm/U1qVKTHZKVkuZOi7njyif1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBHv29%2FbtsEFOz9sOm%2FU1qVKTHZKVkuZOi7njyif1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2100&quot; height=&quot;783&quot; data-filename=&quot;ps.png&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문제 정보&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1707724237446&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;17141번: 연구소 2&quot; data-og-description=&quot;인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이러&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17141&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17141&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bHm4ZR/hyVi9T90bD/MGJsAJmlK5f6t5AIM4BTaK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17141&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17141&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bHm4ZR/hyVi9T90bD/MGJsAJmlK5f6t5AIM4BTaK/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;17141번: 연구소 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이러&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&lt;i&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;⏱️ 풀이 시간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;30.20m&lt;/b&gt;&lt;/i&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;그냥 BFS로 절대 통과 못할 줄 알았는데... 시간이 간당간당하게 맞은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현이랑 섞인 BFS의 대표적인 문제 같은 느낌이었다.&lt;/p&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;b&gt;바이러스를 올릴 수 있는 노드에서 m개 만큼의 조합&lt;/b&gt;으로 BFS를 돌린 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;벽의 노드를 제외한 노드&lt;/b&gt;가 모두 방문처리 되었는지 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 꽤 복잡하게 짜여진 것 같..다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 BFS는..원래그..래ㄹ..걸..?&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; &amp;zwj;  나의 답&lt;/h3&gt;
&lt;pre id=&quot;code_1704335699374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pypy3

import sys
from itertools import combinations
from collections import deque
input = sys.stdin.readline
INF = sys.maxsize

n, m = list(map(int, input().rstrip().split()))
graph = []
nodes = []
walls = []
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
res = INF

for i in range(n):
    row = list(map(int, input().rstrip().split()))
    for j in range(n):
        if row[j] == 2:
            nodes.append((i, j))
        elif row[j] == 1:
            walls.append((i, j))
    graph.append(row)

def bfs(frame):
    global res
    q = deque([])
    temp = 0
    for node in frame:
        q.append(node)
        times[node[0]][node[1]] = 0
    while q:
        px, py = q.popleft()
        for i in range(4):
            nx, ny = px + dx[i], py + dy[i]
            if (0 &amp;lt;= nx &amp;lt; n) and (0 &amp;lt;= ny &amp;lt; n) and (graph[nx][ny] != 1) and (times[px][py] + 1 &amp;lt; times[nx][ny]):
                times[nx][ny] = times[px][py] + 1
                temp = max(temp, times[nx][ny])
                q.append((nx, ny))
    flag = False
    for i in range(n):
        for j in range(n):
            if (i, j) in walls: continue
            if times[i][j] == INF:
                flag = True
                break
    if not flag:
        res = min(res, temp)

for frame in combinations(nodes, m):
    times = [[INF] * n for _ in range(n)]
    bfs(frame)

if res == INF:
    print(-1)
else:
    print(res)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/문제풀이</category>
      <category>BFS</category>
      <category>백준</category>
      <category>코딩테스트</category>
      <author>중규리</author>
      <guid isPermaLink="true">https://99uulog.tistory.com/126</guid>
      <comments>https://99uulog.tistory.com/126#entry126comment</comments>
      <pubDate>Mon, 12 Feb 2024 16:53:40 +0900</pubDate>
    </item>
  </channel>
</rss>