<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발하는만두</title>
    <link>https://dev-youngjun.tistory.com/</link>
    <description>공부한 내용을 정리합니다 :)</description>
    <language>ko</language>
    <pubDate>Sat, 8 Aug 2026 15:01:12 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>youngjun._.</managingEditor>
    <image>
      <title>개발하는만두</title>
      <url>https://tistory1.daumcdn.net/tistory/3515656/attach/767c303289694a56916f11d6263757e6</url>
      <link>https://dev-youngjun.tistory.com</link>
    </image>
    <item>
      <title>24년 회고</title>
      <link>https://dev-youngjun.tistory.com/265</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TBD&lt;/p&gt;</description>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/265</guid>
      <comments>https://dev-youngjun.tistory.com/265#entry265comment</comments>
      <pubDate>Tue, 14 Jan 2025 00:12:20 +0900</pubDate>
    </item>
    <item>
      <title>@ForceInline 애너테이션 왜 쓸까</title>
      <link>https://dev-youngjun.tistory.com/263</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이펙티브자바 서적에 &lt;b&gt;아이템49. 매개변수가 유효한지 검사하라&lt;/b&gt; 스터디 진행 중에 Java9에서 추가된 Objects 클래스의 checkIndex 메서드를 확인해보다가 처음보는 애너테이션이 있어 궁금증에 찾아보았다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// Object class (In openjdk-18.0.2.1)
@ForceInline
public static int checkIndex(int index, int length) {
    return Preconditions.checkIndex(index, length, null);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 java 버전은&amp;nbsp;&lt;a href=&quot;https://jdk.java.net/18/&quot;&gt;openjdk-18.0.2.1&lt;/a&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;javadoc 읽어보기&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A method or constructor may be annotated as&amp;nbsp;&quot;force inline&quot;&amp;nbsp;if the standard inlining metrics are to be ignored when the HotSpot VM inlines the method or constructor.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This annotation must be used sparingly.&amp;nbsp;It is useful when the only reasonable alternative is to bind the name of a specific method or constructor into the HotSpot VM for special handling by the inlining policy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This annotation must not be relied on as an alternative to avoid tuning the VM's inlining policy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In a few cases,&amp;nbsp;it may act as a temporary workaround until the profiling and inlining performed by the HotSpot VM is sufficiently improved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HotSpot VM이 메서드 또는 생성자를 인라인화할 때 표준 인라인 메트릭을 무시할 경우 메서드 또는 생성자에 &quot;인라인화 강제&quot;라는 주석을 달 수 있다.&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;Inline 정책에 의한 특별한 처리를 위해 HotSpot VM에 바인딩하는 것이 유일한 방법일 때 사용해라&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;VM의 inlining 정책을 피하기 위한 대안으로 사용하면 안된다.&lt;/li&gt;
&lt;li&gt;드문 경우에, HotSpot VM의 inlining이 개선될 때까지 임시 해결 방법으로 사용할 수 있다.&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;Inline이란&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 내부에 들어간 함수&lt;/li&gt;
&lt;li&gt;성능때문에 보통 C++에서 많이 다뤘었다.&lt;/li&gt;
&lt;li&gt;함수가 호출될 때마다 발생하는 오버헤드를 줄이기 위해&amp;nbsp;inline&amp;nbsp;키워드를 사용한다.&lt;/li&gt;
&lt;li&gt;최근 C++ 컴파일러는 함수를 적절하게 inline 해주기 때문에 키워드 불필요&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void PrintHello() {
    cout &amp;lt;&amp;lt; &quot;Hello, World!&quot; &amp;lt;&amp;lt; endl;
}

int main() {
    PrintHello();
    PrintHello();
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 inline 하면 아래와 같아진다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int main() {
    cout &amp;lt;&amp;lt; &quot;Hello, World!&quot; &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; &quot;Hello, World!&quot; &amp;lt;&amp;lt; endl;
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Java에서의 inline&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 inline의 필요성과 동작방식에 대한 논의는 여러차례 stack overlow에서 다뤄졌다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1159087/inlining-in-java&quot;&gt;https://stackoverflow.com/questions/1159087/inlining-in-java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/7744210/force-inline-in-java&quot;&gt;https://stackoverflow.com/questions/7744210/force-inline-in-java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 함수 호출의 오버헤드까지 최적화하려 하지 말고,&amp;nbsp;JIT 컴파일러의 최적화를 믿으면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그럼 왜&amp;nbsp;@ForceInline을 선언했나&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명이 있을까 싶어 Oracle docs와 baeldung,&amp;nbsp;SOF를 찾아봤지만 궁금증을 해결하지 못했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#checkIndex-int-int-&quot;&gt;Oracle java9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/java-9-objects-new&quot;&gt;Baeldung : Java 9 java.util.Objects Additions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추측하기로는 HostSpot VM 최적화 과정에 성능 이점을 누리기 위한 조치일 것 같은데 정확한 이유는 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 어떻게 더 조사하면 좋을지 잘 모르겠으니,&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/74502755/why-does-the-java-util-objects-checkindex-method-need-forceinline&quot;&gt;stack overflow에 질문&lt;/a&gt;을 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가 설명해주면.. 정리해보려고한다.&lt;/p&gt;</description>
      <category>Back-End/Java</category>
      <category>Annotation</category>
      <category>Java</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/263</guid>
      <comments>https://dev-youngjun.tistory.com/263#entry263comment</comments>
      <pubDate>Sun, 20 Nov 2022 04:00:17 +0900</pubDate>
    </item>
    <item>
      <title>Notion Enhancer 설정 열기 (단축키 안될 때)</title>
      <link>https://dev-youngjun.tistory.com/262</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;선 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 바로 여는 단축키가 동작하지 않으니, 아래 순서로 열자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Command + Option + i&lt;/span&gt; (Windows : ctrl + shift + i) 로 개발자 도구 열기&lt;/li&gt;
&lt;li&gt;Console tap에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;__enhancerElectronApi.focusMenu()&lt;/span&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;span style=&quot;background-color: #dddddd;&quot;&gt;설정 메뉴 &amp;rarr; core &amp;rarr; menu &amp;rarr; toggle focus hotkey&lt;/span&gt; 수정&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 설정 창 접근이 편해진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion Enhancer 공식 docs에 설명을 보면 다운로드 후 실행하면 설정 탭이 활성화된다고 적혀있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ3Moj/btrQrt5IZ1s/8JtcMrDIK4EE3Dtwp1VNP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ3Moj/btrQrt5IZ1s/8JtcMrDIK4EE3Dtwp1VNP0/img.png&quot; data-alt=&quot;출처 : Notion Enhancer docs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ3Moj/btrQrt5IZ1s/8JtcMrDIK4EE3Dtwp1VNP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ3Moj%2FbtrQrt5IZ1s%2F8JtcMrDIK4EE3Dtwp1VNP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;531&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : Notion Enhancer docs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 저 설정 탭이 활성화되지 않는 이슈가 Github에 많이 올라왔고, Notion 측에서 막고 있는지 잘 수정되지 않고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/notion-enhancer/desktop/issues/762&quot;&gt;https://github.com/notion-enhancer/desktop/issues/762&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정 열기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 1 : 단축키&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 51px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;OS&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;단축키&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Windows OS&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Ctrl + Alt + E&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Mac OS&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Control + Option + E&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 Enhancer에서는 Mac OS 단축키가 잘 동작했던 것 같은데, 잘 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 경우처럼 키보드 타입의 문제인 분들에게 참고가 될까 싶어서 첨부한다.&lt;s&gt;(저는 안되더라구요  )&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdc2ud/btrQqMSkGi7/5lKOZQj0nHIeHt7pHqukHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdc2ud/btrQqMSkGi7/5lKOZQj0nHIeHt7pHqukHK/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://github.com/notion-enhancer/desktop/issues/762#issuecomment-1161356224&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdc2ud/btrQqMSkGi7/5lKOZQj0nHIeHt7pHqukHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdc2ud%2FbtrQqMSkGi7%2F5lKOZQj0nHIeHt7pHqukHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;260&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://github.com/notion-enhancer/desktop/issues/762#issuecomment-1161356224&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 2 : 개발자도구 명령어 실행&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Command + Option + i&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(Windows : ctrl + shift + i) 로 개발자 도구 열기&lt;/li&gt;
&lt;li&gt;Console tap에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;__enhancerElectronApi.focusMenu()&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;입력&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;1480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/njLxa/btrQrtSdjwU/iSoz8iW60AUkKz0Tlm74oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/njLxa/btrQrtSdjwU/iSoz8iW60AUkKz0Tlm74oK/img.png&quot; data-alt=&quot;Notion Enhancer 개발자 도구 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/njLxa/btrQrtSdjwU/iSoz8iW60AUkKz0Tlm74oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnjLxa%2FbtrQrtSdjwU%2FiSoz8iW60AUkKz0Tlm74oK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;622&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;1480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Notion Enhancer 개발자 도구 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 편하려면 설정 창을 여는 단축키를 변경하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;설정 메뉴 &amp;rarr; core &amp;rarr; menu &amp;rarr; toggle focus hotkey&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;수정&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3172&quot; data-origin-height=&quot;1658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcMH0M/btrQrGX4eh6/7KyScPq5emG46MgJqma9mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcMH0M/btrQrGX4eh6/7KyScPq5emG46MgJqma9mk/img.png&quot; data-alt=&quot;설정 메뉴 접근 단축키 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcMH0M/btrQrGX4eh6/7KyScPq5emG46MgJqma9mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcMH0M%2FbtrQrGX4eh6%2F7KyScPq5emG46MgJqma9mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;450&quot; data-origin-width=&quot;3172&quot; data-origin-height=&quot;1658&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;</description>
      <category>Tool</category>
      <category>notion</category>
      <category>Notion Enhancer</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/262</guid>
      <comments>https://dev-youngjun.tistory.com/262#entry262comment</comments>
      <pubDate>Sat, 5 Nov 2022 14:32:33 +0900</pubDate>
    </item>
    <item>
      <title>@Bean과 static method</title>
      <link>https://dev-youngjun.tistory.com/261</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;인프런 강의(&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/unit/55345&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스프링 핵심 원리 기본&lt;/a&gt;) 내용 중 Spring Container는 Bean을 싱글톤으로 관리하고 이를 확인하는 테스트 코드를 실행하다가 &lt;u&gt;&lt;b&gt;@Configuration class에서 Bean을 등록하는 method가 static인 경우 싱글톤이 보장되지 않아&lt;/b&gt;&lt;/u&gt; 이유가 궁금해서 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;기존 코드&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667129128409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ConfigurationSingletonTest {

    @Test
    void configurationTest() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        MemberServiceImpl memberService = ac.getBean(&quot;memberService&quot;, MemberServiceImpl.class);
        OrderServiceImpl orderService = ac.getBean(&quot;orderService&quot;, OrderServiceImpl.class);
        MemberRepository memberRepository = ac.getBean(&quot;memberRepository&quot;, MemberRepository.class);

        MemberRepository memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();

        // 값 3개가 같다
        System.out.println(&quot;memberService -&amp;gt; memberRepository1 = &quot; + memberRepository1);
        System.out.println(&quot;orderService -&amp;gt; memberRepository2 = &quot; + memberRepository2);
        System.out.println(&quot;memberRepository -&amp;gt; memberRepository3 = &quot; + memberRepository);

        assertSame(memberRepository1, memberRepository2);
        assertSame(memberRepository1, memberRepository);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fa1ve/btrPZakhZj8/sdkFQlpzhggp06dDS82W2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fa1ve/btrPZakhZj8/sdkFQlpzhggp06dDS82W2K/img.png&quot; data-alt=&quot;정상적으로 테스트가 통과된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fa1ve/btrPZakhZj8/sdkFQlpzhggp06dDS82W2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFa1ve%2FbtrPZakhZj8%2FsdkFQlpzhggp06dDS82W2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;220&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;580&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;수정된 코드&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public static MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository());
    }
}&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;memberRepository() method를 static으로 변경했을 뿐인데 싱글톤이 보장되지 않아 테스트는 실패한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R2zqu/btrPVETIDAe/P24V3TxedGGkVwSMolefsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R2zqu/btrPVETIDAe/P24V3TxedGGkVwSMolefsK/img.png&quot; data-alt=&quot;싱글톤이 보장되지 않는다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R2zqu/btrPVETIDAe/P24V3TxedGGkVwSMolefsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR2zqu%2FbtrPVETIDAe%2FP24V3TxedGGkVwSMolefsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;220&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;싱글톤이 보장되지 않는다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;원인 분석&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;@Bean 애너테이션에 어떤 로직이 숨어있는지 먼저 파악해보기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GVSjA/btrPQJhLc1s/cG6hj4k2NXuMRSRC07zY80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GVSjA/btrPQJhLc1s/cG6hj4k2NXuMRSRC07zY80/img.png&quot; data-alt=&quot;Bean 애너테이션 javaDoc의 static 관련 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GVSjA/btrPQJhLc1s/cG6hj4k2NXuMRSRC07zY80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGVSjA%2FbtrPQJhLc1s%2FcG6hj4k2NXuMRSRC07zY80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;769&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;1262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Bean 애너테이션 javaDoc의 static 관련 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1. static 키워드 때문에 BFPP로 인식하는 것 같다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 @Bean 애너테이션을 보면 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. BeanFactoryPostProcessor(BFPP) 객체는 컨테이너 생명주기 초기에 인스턴스화 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. BFPP 객체를 초기에 인스턴스화 하면 @Autowired, @Value, @PostConsruct 같은 애너테이션의 처리를 방해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 이를 예방하기 위해 BFPP를 리턴하는 @Bean 메서드를 static으로 선언하면 된다.&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;span&gt;2. BFPP는 뭘까&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; BeanFactoryPostProcessor는 해석 그대로 Bean Factory 후처리기이다. (&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;javaDoc link&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Bean이 초기화 되는 과정은 아래 그림이 가장 이해하기 쉬워서 가져왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/no2WK/btrPT0WOluO/jasNaxFEtrXuWfnXEQSV7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/no2WK/btrPT0WOluO/jasNaxFEtrXuWfnXEQSV7k/img.png&quot; data-alt=&quot;출처 : https://www.dineshonjava.com/bean-lifecycle-and-callbacks/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/no2WK/btrPT0WOluO/jasNaxFEtrXuWfnXEQSV7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fno2WK%2FbtrPT0WOluO%2FjasNaxFEtrXuWfnXEQSV7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;523&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://www.dineshonjava.com/bean-lifecycle-and-callbacks/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 처음 &lt;b&gt;Load Bean Definitions&lt;/b&gt; 의 Post Process 부분이 BFPP이고 Bean의 정의 자체를 바꿀 수 있다.&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;BFPP 구현 예제는 &lt;a href=&quot;https://www.dineshonjava.com/writing-beanfactorypostprocessor-in/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring Core article&lt;/a&gt;에서 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 원리와 static 으로 선언해야 하는 이유에 대한 자세한 내용은 &lt;a href=&quot;https://mangkyu.tistory.com/177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다른 블로그 글&lt;/a&gt;을 참고하면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;정리하면, Bean을 등록하는 메서드에 static 키워드가 붙으면 Spring 컨테이너의 life sycle 매우 초기에 다른 Bean들보다 먼저 등록된다. 따라서 @Configuration 클래스들이 생성되기 전에 호출이 가능하다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;추가로 refreshContext 동작 과정도 공부해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- &lt;a href=&quot;https://mangkyu.tistory.com/214&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mangkyu.tistory.com/214&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Back-End/Spring</category>
      <category>bean</category>
      <category>BFPP</category>
      <category>Java</category>
      <category>Spring</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/261</guid>
      <comments>https://dev-youngjun.tistory.com/261#entry261comment</comments>
      <pubDate>Sun, 30 Oct 2022 21:47:34 +0900</pubDate>
    </item>
    <item>
      <title>MySQL User 추가 및 권한 부여</title>
      <link>https://dev-youngjun.tistory.com/260</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Docker로 MySQL DB를  띄우면 사용자를 생성하고 권한을 부여해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mysql Container에 접속해서 Database와 User를 생성하고 해당 User에게 DB에 대한 모든 권한을 부여하는 명령어를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. Docker Container 접속&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;docker로 구성하지 않았으면 MySQL 서버에 접속하면 됩니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1659730834994&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -it {컨테이너_이름} bash

# docker container(or server) 접속 후
mysql -u root -p
password: {root_password}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. Database 생성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;show databases;&lt;/span&gt;&lt;/i&gt; 조회했을 때 필요한 DB가 없다면 생성부터 해주자.&lt;/p&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;# DB 생성
CREATE DATABASE blog;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;3. User 추가&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;# 사용자 생성
CREATE USER yj_park@'%' IDENTIFIED BY '1234';&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host 를 '%' 로 주면 모든 외부 IP에서 접속할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특정 IP 대역에서만 접속하게 설정&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;하려면&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;'IP.%'&lt;/i&gt;&lt;/span&gt; 로 주면 된다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;'172.168.%'&lt;/i&gt;&lt;/span&gt;&amp;nbsp; : IP 가 172.168.xxx.xxx&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;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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; background-color: #f6e199;&quot;&gt;4. DB 권한 부여&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;GRANT ALL PRIVILEGES
    ON blog.*
    TO yj_park@'%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PRIVILEGES ON .&lt;/b&gt; &amp;rarr; 모든 DB 권한을 준다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GRANT SELECT, INSERT, UPDATE ON DB명.* TO '아이디'@'%';&lt;/b&gt; &amp;rarr; 특정 DB에 대한 특정 권한(CRU) 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;FLUSH PRIVILEGES;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 FLUSE 명령어를 통해 &lt;b&gt;권한 적용을 서버에 반영&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;5. USER 권한 확인하기&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SHOW GRANTS FOR yj_park@'%';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5RuAp/btrI0eIDx86/hwMynNM1eWE1ryeKn7vpqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5RuAp/btrI0eIDx86/hwMynNM1eWE1ryeKn7vpqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5RuAp/btrI0eIDx86/hwMynNM1eWE1ryeKn7vpqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5RuAp%2FbtrI0eIDx86%2FhwMynNM1eWE1ryeKn7vpqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;191&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;276&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;span style=&quot;background-color: #f6e199;&quot;&gt;6. USER 삭제하기&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;# DROP USER {user명}@{server명};
DROP USER yj_park@localhost;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database</category>
      <category>MySQL</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/260</guid>
      <comments>https://dev-youngjun.tistory.com/260#entry260comment</comments>
      <pubDate>Sun, 7 Aug 2022 12:12:26 +0900</pubDate>
    </item>
    <item>
      <title>로컬에 Docker 기반 Kafka 서버 구축하고 테스트</title>
      <link>https://dev-youngjun.tistory.com/259</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;먼저 docker가 설치되어있어야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 환경에 Docker를 통해 Kafka 서버를 구축해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka docker image는 wurstmeister/kafka 가 가장 많이 사용되고 있다. &lt;a href=&quot;https://github.com/wurstmeister/kafka-docker&quot;&gt;Github&lt;/a&gt;에서 Release 버전 및 &lt;a href=&quot;http://wurstmeister.github.io/kafka-docker/&quot;&gt;튜토리얼&lt;/a&gt;도 확인이 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. docker-compose.yml 작성 및 실행&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka를 통한 pub/sub 기능만 테스트해볼 것이기 때문에 싱글 브로커만 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, topic의 Partition과 Replica 개수도 각각 하나씩만 설정하였다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;version: '2'
services:
  zookeeper:
    container_name: local-zookeeper
    image: wurstmeister/zookeeper:3.4.6
    ports:
      - &quot;2181:2181&quot;
  kafka:
    container_name: local-kafka
    image: wurstmeister/kafka:2.13-2.8.1
    depends_on:
      - zookeeper
    ports:
      - &quot;9092:9092&quot;
    environment:
      KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 # 싱글 브로커 환경
      KAFKA_ADVERTISED_PORT: 9092
      KAFKA_CREATE_TOPICS: &quot;chat:1:1&quot; # Topic 명 : Partition 개수 : Replica 개수
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 : docker-compose up -d&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 : docker-compose down&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhxzEj/btrI1UQqQVj/fbpiNCGwPQBGq7e2bcdS70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhxzEj/btrI1UQqQVj/fbpiNCGwPQBGq7e2bcdS70/img.png&quot; data-alt=&quot;컨테이너 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhxzEj/btrI1UQqQVj/fbpiNCGwPQBGq7e2bcdS70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhxzEj%2FbtrI1UQqQVj%2FfbpiNCGwPQBGq7e2bcdS70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;138&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;208&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;Docker Desktop에서 아래와 같이 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2764&quot; data-origin-height=&quot;1664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXswH/btrI3xGB9Pt/516wgjQyuW63mVIGKfTnk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXswH/btrI3xGB9Pt/516wgjQyuW63mVIGKfTnk1/img.png&quot; data-alt=&quot; Docker Desktop&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXswH/btrI3xGB9Pt/516wgjQyuW63mVIGKfTnk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXswH%2FbtrI3xGB9Pt%2F516wgjQyuW63mVIGKfTnk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;518&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2764&quot; data-origin-height=&quot;1664&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Docker Desktop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;docker ps - a&lt;/i&gt;&lt;/span&gt; 명령어로 확인해 볼 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;3018&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/seRJ3/btrI2egPXCX/QbRkWIwrjqDqtq9kJ0SsH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/seRJ3/btrI2egPXCX/QbRkWIwrjqDqtq9kJ0SsH1/img.png&quot; data-alt=&quot; 명령어로 컨테이너 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/seRJ3/btrI2egPXCX/QbRkWIwrjqDqtq9kJ0SsH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FseRJ3%2FbtrI2egPXCX%2FQbRkWIwrjqDqtq9kJ0SsH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;153&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;3018&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 명령어로 컨테이너 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. Kafka 테스트&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에 Kafka를 또 설치하지 말고, Docker 컨테이너에 접근해서 테스트해보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-1. docker 컨테이너 접속 후 topic 확인&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker container exec -it local-kafka bash

# 컨테이너 서버 접속 후
kafka-topics.sh --list --bootstrap-server localhost:9092&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;local-kafka 부분은 본인이 띄운 docker 컨테이너의 NAMES를 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KVc5F/btrI0eu7DQW/3v1zXBwDFbRVqiPMW4nC3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KVc5F/btrI0eu7DQW/3v1zXBwDFbRVqiPMW4nC3k/img.png&quot; data-alt=&quot;docker container 접속 후 topic 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KVc5F/btrI0eu7DQW/3v1zXBwDFbRVqiPMW4nC3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKVc5F%2FbtrI0eu7DQW%2F3v1zXBwDFbRVqiPMW4nC3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;249&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker container 접속 후 topic 확인&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;현재 Kafka에 토픽은 &lt;b&gt;chat&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;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;docker-compose.yml&lt;/i&gt;&lt;/span&gt; 파일에 18 line &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;KAFKA_CREATE_TOPICS: &quot;chat:1:1&quot;&lt;/i&gt;&lt;/span&gt; 으로 토픽을 먼저 생성했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka 관련 명령어는 &lt;a href=&quot;https://kafka.apache.org/quickstart&quot;&gt;공식문서 Quick Start&lt;/a&gt; 에 자세히 설명되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-2. kafka 메시지 pub/sub 테스트&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널 2개에 각각 접속해서 producer, consumer 테스트를 해보자.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;docker container exec -it local-kafka bash

# Producer 실행
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic chat

# Consumer 실행
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic chat
&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;kafka-test1.gif&quot; data-origin-width=&quot;2148&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3jx3O/btrI1Oo1370/SbiBN25QFkxNKVOkHAlAr1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3jx3O/btrI1Oo1370/SbiBN25QFkxNKVOkHAlAr1/img.gif&quot; data-alt=&quot;카프카 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3jx3O/btrI1Oo1370/SbiBN25QFkxNKVOkHAlAr1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b3jx3O/btrI1Oo1370/SbiBN25QFkxNKVOkHAlAr1/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;2148&quot; height=&quot;836&quot; data-filename=&quot;kafka-test1.gif&quot; data-origin-width=&quot;2148&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;카프카 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://pearlluck.tistory.com/638&quot;&gt;https://pearlluck.tistory.com/638&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@jinmin2216/Kafka-Docker-설치부터-Kafka-Quick-Start까지&quot;&gt;https://velog.io/@jinmin2216/Kafka-Docker-설치부터-Kafka-Quick-Start까지&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/quickstart&quot;&gt;https://kafka.apache.org/quickstart&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BigData/Kafka</category>
      <category>docker</category>
      <category>docker-compose</category>
      <category>Kafka</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/259</guid>
      <comments>https://dev-youngjun.tistory.com/259#entry259comment</comments>
      <pubDate>Sat, 6 Aug 2022 02:43:08 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot Swagger 3.0 적용하기</title>
      <link>https://dev-youngjun.tistory.com/258</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드 적용 과정은&amp;nbsp;&lt;a href=&quot;https://github.com/jun108059/chat-server/issues/16&quot; data-hovercard-type=&quot;issue&quot; data-hovercard-url=&quot;/jun108059/chat-server/issues/16/hovercard&quot;&gt;github issue&lt;/a&gt;&amp;nbsp;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;springdoc 과 springfox 비교&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://springdoc.org/&quot;&gt;springdoc 공식문서&lt;/a&gt;를 참고해서 작성&lt;/li&gt;
&lt;li&gt;작성일(22.07.30) 기준 최신버전 : springdoc-openapi v1.6.9&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;a id=&quot;user-content-1-springdoc-vs-springfox&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#1-springdoc-vs-springfox&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. springdoc vs springfox&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 라이브러리 모두 Spring Framework를 사용하는 프로젝트에서 Swagger를 이용해 API 문서를 쉽게 쓸 수 있도록 해주는 라이브러리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2012 ~ 2015 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Swagger SpringMVC&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이름으로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;springfox&lt;/span&gt; 라이브러리가 많이 사용 됨&lt;/li&gt;
&lt;li&gt;2015 ~ 2018 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;springfox-swagger&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;업데이트가 활발히 이루어짐&lt;/li&gt;
&lt;li&gt;2018 ~ 2020 : fox 업데이트가 진행되지 않아&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;springdoc&lt;/span&gt;&lt;span&gt; 개발되어 많이 사용 됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2020 ~ 2022 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;springfox&lt;/span&gt;도 다시 업데이트 시작하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;springdoc&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비슷하게 활용 중&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C60Mr/btrIwHRKL7Z/6KUHPGD8TSU6uvvpHSNPTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C60Mr/btrIwHRKL7Z/6KUHPGD8TSU6uvvpHSNPTK/img.png&quot; data-alt=&quot;(출처 : Google Trends)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C60Mr/btrIwHRKL7Z/6KUHPGD8TSU6uvvpHSNPTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC60Mr%2FbtrIwHRKL7Z%2F6KUHPGD8TSU6uvvpHSNPTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1166&quot; height=&quot;671&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(출처 : Google Trends)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-1-1-springdoc-선택&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#1-1-springdoc-%EC%84%A0%ED%83%9D&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;1-1. springdoc 선택&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;springdoc을 선택했다. 공식문서가 읽기 편했고, API 그룹핑 설정이 간단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성일 기준 더 활발히 업데이트 되고 있기도 했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;a id=&quot;user-content-swagger-openapi-30&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#swagger-openapi-30&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;b&gt;Swagger OpenAPI 3.0&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라이브러리가 OpenAPI 3.0 스펙에 맞는 JSON을 자동으로 만들어 주면, Swagger UI는 만들어진 JSON을 화면에 표시 해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5g5Xq/btrIxTDYMR0/boTizSFaI1FVfYKRwY1L8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5g5Xq/btrIxTDYMR0/boTizSFaI1FVfYKRwY1L8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5g5Xq/btrIxTDYMR0/boTizSFaI1FVfYKRwY1L8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5g5Xq%2FbtrIxTDYMR0%2FboTizSFaI1FVfYKRwY1L8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;944&quot; height=&quot;363&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://springdoc.org/&quot;&gt;springdoc 공식문서&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;springdoc-openapi-ui를 살펴보면 swagger-ui를 포함하고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;swagger-ui&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 핵심 로직 구현&lt;/li&gt;
&lt;li&gt;springdoc-openapi&lt;span&gt;&amp;nbsp;&lt;/span&gt;: swagger-ui를 추상화해서 더 잘 활용할 수 있게 해주는 라이브러리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-2-프로젝트에-적용&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#2-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. 프로젝트에 적용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gradle, yml 기반으로 적용했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-2-1-dependency-추가&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#2-1-dependency-%EC%B6%94%EA%B0%80&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-1. dependency 추가&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;// build.gradle 파일

dependencies {
    implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-2-2-yml-설정-추가&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#2-2-yml-%EC%84%A4%EC%A0%95-%EC%B6%94%EA%B0%80&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-2. yml 설정 추가&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;springdoc:
  swagger-ui:
    path: /swagger-ui.html
    groups-order: DESC
    operationsSorter: method
    disable-swagger-default-url: true
    display-request-duration: true
  api-docs:
    path: /api-docs
  show-actuator: true
  default-consumes-media-type: application/json
  default-produces-media-type: application/json
  paths-to-match:
    - /v1/**&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-2-3-swaggerconfig-설정-등록&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#2-3-swaggerconfig-%EC%84%A4%EC%A0%95-%EB%93%B1%EB%A1%9D&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-3. SwaggerConfig 설정 등록&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;// SwaggerConfig.java
@OpenAPIDefinition(
        info = @Info(title = &quot;채팅서비스 API 명세서&quot;,
                description = &quot;헥사고날 아키텍처 기반 채팅 서비스 API 명세서&quot;,
                version = &quot;v1&quot;))
@RequiredArgsConstructor
@Configuration
public class SwaggerConfig {

    @Bean
    public GroupedOpenApi chatOpenApi() {
        String[] paths = {&quot;/v1/**&quot;};

        return GroupedOpenApi.builder()
                .group(&quot;채팅서비스 API v1&quot;)
                .pathsToMatch(paths)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 JWT 토큰 관련 정보도 여기에 추가해주면 좋겠다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-2-4-컨트롤러에-적용&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#2-4-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%97%90-%EC%A0%81%EC%9A%A9&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-4. 컨트롤러에 적용&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// DeleteMemberController.java
@RestController
@RequiredArgsConstructor
public class DeleteMemberController {

    @Operation(summary = &quot;회원 탈퇴 요청&quot;, description = &quot;회원 정보가 삭제됩니다.&quot;, tags = { &quot;Member Controller&quot; })
    @ApiResponses({
            @ApiResponse(responseCode = &quot;200&quot;, description = &quot;OK&quot;,
                    content = @Content(schema = @Schema(implementation = DeleteMemberResponse.class))),
            @ApiResponse(responseCode = &quot;400&quot;, description = &quot;BAD REQUEST&quot;),
            @ApiResponse(responseCode = &quot;404&quot;, description = &quot;NOT FOUND&quot;),
            @ApiResponse(responseCode = &quot;500&quot;, description = &quot;INTERNAL SERVER ERROR&quot;)
    })
    @DeleteMapping(&quot;/v1/member/{id}&quot;)
    ResponseEntity&amp;lt;DeleteMemberResponse&amp;gt; deleteMember(
            @Parameter(description = &quot;회원 ID&quot;, required = true, example = &quot;1&quot;) @PathVariable(&quot;id&quot;) Long id) {
        // 생략..
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-3-띄워서-확인&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#3-%EB%9D%84%EC%9B%8C%EC%84%9C-%ED%99%95%EC%9D%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;3. 띄워서 확인&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 URL&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://localhost:8080/swagger-ui.html&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfGDnq/btrIvnyPLJO/VFsiPAUc7VRUW9OK1VbPFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfGDnq/btrIvnyPLJO/VFsiPAUc7VRUW9OK1VbPFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfGDnq/btrIvnyPLJO/VFsiPAUc7VRUW9OK1VbPFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfGDnq%2FbtrIvnyPLJO%2FVFsiPAUc7VRUW9OK1VbPFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1297&quot; height=&quot;893&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;893&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a id=&quot;user-content-4-정리&quot; href=&quot;https://github.com/jun108059/chat-server/blob/main/docs/04_Swagger.md#4-%EC%A0%95%EB%A6%AC&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;4. 정리&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;br /&gt;&lt;/span&gt;Code Base 에 Swagger 관련 코드가 많이 들어가게 되는 문제를 해결하기 위해 TDD에 더 익숙해지면 RestDoc으로 변경해보려고 한다. &lt;br /&gt;&lt;br /&gt;최근에 스터디하면서 관련 내용을 논의한 적이 있는데, 어떤 분이 컨트롤러 인터페이스를 만들어서 Swagger 관련 내용을 인터페이스에 작성한 것을 봤는데 그것도 좋은 방법인 것 같다!&lt;br /&gt;&lt;br /&gt;리펙터링할때 한번 적용해보면 좋을 것 같다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://springdoc.org/&quot;&gt;springdoc 공식문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://springfox.github.io/springfox/docs/current/&quot;&gt;springfox-swagger 공식문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Back-End/Spring</category>
      <category>Spring</category>
      <category>SpringDoc</category>
      <category>springfox</category>
      <category>swagger</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/258</guid>
      <comments>https://dev-youngjun.tistory.com/258#entry258comment</comments>
      <pubDate>Sat, 30 Jul 2022 02:37:32 +0900</pubDate>
    </item>
    <item>
      <title>Docker로 MySQL 띄우고 IntelliJ 연결하기</title>
      <link>https://dev-youngjun.tistory.com/256</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Docker 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop&quot;&gt;Docker 홈페이지&lt;/a&gt;에 접속하여 자신의 OS에 맞는 Docker를 내려 받아 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 다음 명령어를 실행하여 버전을 출력해 보자.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ docker -v
Docker version 20.10.16, build aa7e414&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. MySQL Docker 이미지 다운로드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 MySQL Docker 이미지를 다운로드한다. 태그에 버전을 지정하지 않으면 최신 버전을 다운로드한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ docker pull mysql

Using default tag: latest
latest: Pulling from library/mysql
bb79b6b2107f: Pull complete
49e22f6fb9f7: Pull complete
842b1255668c: Pull complete
9f48d1f43000: Pull complete
c693f0615bce: Pull complete
8a621b9dbed2: Pull complete
0807d32aef13: Pull complete
a56aca0feb17: Pull complete
de9d45fd0f07: Pull complete
1d68a49161cc: Pull complete
d16d318b774e: Pull complete
49e112c55976: Pull complete
Digest: sha256:8c17271df53ee3b843d6e16d46cff13f22c9c04d6982eb15a9a47bd5c9ac7e2d
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 버전을 지정하려면 태그에 버전을 지정한다. 다운로드할 수 있는 MySQL 버전은&amp;nbsp;&lt;a href=&quot;https://hub.docker.com/_/mysql/?tab=tags&quot;&gt;docker hub&lt;/a&gt;에서 확인할 수 있다. 예를 들어, MySQL 8.0.22 버전을 다운로드하려면 다음과 같이 태그에 버전을 지정한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ docker pull mysql:8.0.22

8.0.22: Pulling from library/mysql
Digest: sha256:8c17271df53ee3b843d6e16d46cff13f22c9c04d6982eb15a9a47bd5c9ac7e2d
Status: Downloaded newer image for mysql:8.0.22
docker.io/library/mysql:8.0.22&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 다운로드한 Docker 이미지를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               8.0.22              db2b37ec6181        2 weeks ago         545MB
mysql               latest              db2b37ec6181        2 weeks ago         545MB&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. MySQL Docker 컨테이너 생성 및 실행&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=&amp;lt;password&amp;gt; -d -p 3306:3306 mysql:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Docker 컨테이너 리스트 출력&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ docker ps -a
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. MySQL Docker 컨테이너 시작/중지/재시작&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# MySQL Docker 컨테이너 중지
$ docker stop mysql-container

# MySQL Docker 컨테이너 시작
$ docker start mysql-container

# MySQL Docker 컨테이너 재시작
$ docker restart mysql-container&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. MySQL Docker 컨테이너 접속&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ docker exec -it mysql-container bash
root@dc557b92f573:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \\g.
Your MySQL connection id is 9
Server version: 8.0.22 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.

mysql&amp;gt; show databases;
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. MySQL 유저 생성&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1657117373997&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Shell
mysql -u root -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwcXZy/btrGF7cO9fm/s5krWjFIkgXBX5BbvOk4O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwcXZy/btrGF7cO9fm/s5krWjFIkgXBX5BbvOk4O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwcXZy/btrGF7cO9fm/s5krWjFIkgXBX5BbvOk4O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwcXZy%2FbtrGF7cO9fm%2Fs5krWjFIkgXBX5BbvOk4O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;441&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 MySQL 사용자를 생성해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1657117602752&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# MySQL 접근 후
CREATE USER '아이디'@'%' IDENTIFIED BY '비밀번호';&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;host 를 '%' 로 주면&amp;nbsp;&lt;b&gt;모든 외부 IP에서 접속&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 IP 대역에서만 접속하게 설정&lt;/b&gt;하려면&amp;nbsp;'IP.%'&amp;nbsp;로 주면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;'172.168.%' -&amp;gt; IP 가 172.168.xxx.xxx 에서만 접속&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. DB 권한 부여&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1657117653247&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# DB 권한 부여
GRANT ALL PRIVILEGES
    ON {DB_NAME}.*
    TO {아이디}@localhost;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;PRIVILEGES ON *&lt;/span&gt; : 모든 DB에 권한을 준다&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;PRIVILEGES&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;ON {DB_NAME}.*&lt;/span&gt; : 특정 DB에만 권한 부여&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;GRANT &lt;i&gt;SELECT, INSERT, UPDATE&lt;/i&gt; ON ON&lt;/span&gt; : 특정 DB에 대한 SELECT, INSERT, UPDATE 권한 선택적으로 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657117942779&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 최종 권한 적용시키기
FLUSH PRIVILEGES;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. 유저 삭제하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1657118070176&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DROP USER {USER_NAME}@{SERVER_URL};
# DROP USER yj_park@localhost;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;9. 외부 접속 확인&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;1698&quot; data-origin-height=&quot;1550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O3vEn/btrGEwLqL09/Sr8eGPKEFVvttbzGklktR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O3vEn/btrGEwLqL09/Sr8eGPKEFVvttbzGklktR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O3vEn/btrGEwLqL09/Sr8eGPKEFVvttbzGklktR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO3vEn%2FbtrGEwLqL09%2FSr8eGPKEFVvttbzGklktR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;639&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;따란~&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://poiemaweb.com/docker-mysql&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://poiemaweb.com/docker-mysql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@gillog/MySQL-%EC%9C%A0%EC%A0%80-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@gillog/MySQL-%EC%9C%A0%EC%A0%80-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Infra/Docker</category>
      <category>docker</category>
      <category>intellij</category>
      <category>MySQL</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/256</guid>
      <comments>https://dev-youngjun.tistory.com/256#entry256comment</comments>
      <pubDate>Wed, 6 Jul 2022 23:47:53 +0900</pubDate>
    </item>
    <item>
      <title>Java : 배열 &amp;harr;️ 리스트 변환 (int형 배열 포함)</title>
      <link>https://dev-youngjun.tistory.com/240</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 배열 ➡️ 리스트&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrays.asList()&lt;/li&gt;
&lt;li&gt;Stream&amp;nbsp;:&amp;nbsp;Collectors.toList()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;1-1. Arrays.asList()&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;String[] arr = { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; };

// Shallow Copy
List&amp;lt;String&amp;gt; list = Arrays.asList(arr);

// Deep Copy (권장)
List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;(Arrays.asList(arr));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deep Copy&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;기존&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;배열과&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;별개의&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;List를&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;생성하게&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;되어&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;추가/변경/삭제에&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;자유롭기&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;때문에&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;권장&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;1-2. Collectors.toList()&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Deep Copy&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;String[] strArr = {&quot;2&quot;, &quot;3&quot;, &quot;1&quot;};

List&amp;lt;String&amp;gt; collect = Arrays.stream(strArr)
        .collect(Collectors.toList());
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. 리스트 ➡️ 배열&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;2-1. toArray()&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;인자로&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;넘겨주는&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;배열&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;size는&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;자동으로&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;조절&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;됨&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;span&gt;크면&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;채워짐&lt;/span&gt;)&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(&quot;1&quot;);
list.add(&quot;2&quot;);

String[] strArr = list.toArray(new String[0]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;3. 배열 ➡️ 리스트 (Primitive 타입)&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;For loop&lt;/li&gt;
&lt;li&gt;Arrays.stream().boxed().collect()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;3-1. For loop&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int[] intArr = {3, 2, 1};

// int -&amp;gt; List
List&amp;lt;Integer&amp;gt; intList = new ArrayList&amp;lt;&amp;gt;();
for (int item : intArr) {
    intList.add(item);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;3-2. Stream 활용&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arrays.stream().boxed().collect()&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;int[] intArr = {3, 2, 1};

// int -&amp;gt; List
List&amp;lt;Integer&amp;gt; intList = Arrays.stream(arr)
                            .boxed()
                            .collect(Collectors.toList());
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;boxed&lt;/span&gt;()&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;메소드는&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Primitive Stream&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;값을&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Wrapper Class로&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;바꿔준다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;4. 리스트 ➡️ 배열 (Primitive 타입)&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;4-1.Stream().mapToInt()&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numlist = new ArrayList&amp;lt;&amp;gt;();
list.add(1);
list.add(2);

int[] numArray = numList.stream()
                    .mapToInt(i -&amp;gt; i)
                    .toArray();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mapToInt()&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드가 IntStream(int 값의 시퀀스)을 반환&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Back-End/Java</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/240</guid>
      <comments>https://dev-youngjun.tistory.com/240#entry240comment</comments>
      <pubDate>Sun, 27 Mar 2022 02:57:35 +0900</pubDate>
    </item>
    <item>
      <title>Java : Stream 문자열 배열 정렬(내림차순, compareTo 재구현)</title>
      <link>https://dev-youngjun.tistory.com/238</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1.문자열 배열 기본 정렬&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 문자열을 Stream을 통해 정렬할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;오름차순&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;Arrays.stream(strArr) .sorted()&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;String[] strArr = {&quot;2&quot;, &quot;3&quot;, &quot;1&quot;};
String sortedStr = Arrays.stream(strArr)
        .sorted()
        .collect(Collectors.joining());
System.out.println(&quot;sortedStr = &quot; + sortedStr);

/* 실행 결과
sortedStr = 123
 */
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;내림차순&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;Arrays.stream(strArr) .sorted(Comparator.reverseOrder())&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;String[] strArr = {&quot;2&quot;, &quot;3&quot;, &quot;1&quot;};
String sortedStr = Arrays.stream(strArr)
        .sorted(Comparator.reverseOrder())
        .collect(Collectors.joining());
System.out.println(&quot;sortedStr = &quot; + sortedStr);

/* 실행 결과
sortedStr = 321
 */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2.문자열 배열 응용 정렬&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 풀다보면 정렬 조건이 다양하게 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하기 위해 compareTo 조건을 바꿀 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;문자열 조합 중 가장 큰 수&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;sorted((s1, s2) -&amp;gt; (s2 + s1).compareTo(s1 + s2))&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;쉽게 응용하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;메소드&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;레퍼런스를&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;활용하여,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;내부&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;함수를&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;호출하고&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;로직은&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;compare&lt;/span&gt;()&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;함수에서&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;구현해준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Solution {

    public String solution(int[] numbers) {
        String[] strArr = {&quot;2&quot;, &quot;3&quot;, &quot;1&quot;};
        String sortedStr = Arrays.stream(strArr)
                .sorted(Solution::compare)
                .collect(Collectors.joining());
        System.out.println(&quot;sortedStr = &quot; + sortedStr);
    }
    
    private int compare(String s1, String s2) {
        // compareTo를 재구현해도 된다.
        return (s2 + s1).compareTo(s1 + s2);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Back-End/Java</category>
      <category>CompareTo</category>
      <category>Java</category>
      <category>sort</category>
      <category>Stream</category>
      <category>내림차순</category>
      <author>youngjun._.</author>
      <guid isPermaLink="true">https://dev-youngjun.tistory.com/238</guid>
      <comments>https://dev-youngjun.tistory.com/238#entry238comment</comments>
      <pubDate>Sat, 26 Mar 2022 14:43:04 +0900</pubDate>
    </item>
  </channel>
</rss>