<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>조햄의 쪼렙 탈출기</title>
    <link>https://jo5ham.tistory.com/</link>
    <description>(현) 캐럿 (패러닷) 에서 일하는 서버 개발자
(전) 네이버 지도에서 일하는 BE 개발자
  Yonsei Univ. Computer Science</description>
    <language>ko</language>
    <pubDate>Sat, 13 Jun 2026 13:49:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>!쪼렙조햄</managingEditor>
    <image>
      <title>조햄의 쪼렙 탈출기</title>
      <url>https://tistory1.daumcdn.net/tistory/3825837/attach/7dd79c7a6f22498e9c54b794b410bb03</url>
      <link>https://jo5ham.tistory.com</link>
    </image>
    <item>
      <title>유저 닉네임 검색 속도 개선 - Postgresql 문자열 검색 빨리하기</title>
      <link>https://jo5ham.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 성과부터 보여드립니다.&lt;/p&gt;
&lt;h1&gt;성과&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 전&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;select * from user_table where nickname ilike '코%' order by id desc limit 100&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;postgresql 의 explain analyze 해본 결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost : 114404.84&lt;br /&gt;Execution Time : 2571.743ms&lt;br /&gt;정직한 Parellel Seq Scan&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 후&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;select * from user_table where nickname ilike '코%' order by id desc limit 100&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;postgresql 의 explain analyze 해본 결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost : 743&lt;br /&gt;Execution Time : 0.8ms&lt;/p&gt;
&lt;h1&gt;문제&lt;/h1&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;유저는 몇명정도? -&amp;gt; X00만명&lt;br /&gt;검색이 어떻게 되어야 함? -&amp;gt; &quot;코&quot; 를 검색했을때, &quot;코&quot;-&amp;gt; &quot;코*&quot;, &quot;&lt;i&gt;코&lt;/i&gt;&quot; 순서로 나와야 함&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개선사항 1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;i&gt;코&lt;/i&gt; 로 죄다 검색해서, 코 -&amp;gt; 코* -&amp;gt; &lt;i&gt;코&lt;/i&gt; 순서로 정렬했었는데,&lt;br /&gt;&lt;i&gt;코&lt;/i&gt; 로 검색하는게 오래걸리다보니&lt;br /&gt;코, 코* 만 검색해서 100개 이상의 결과가 나오면,&lt;br /&gt;&lt;i&gt;코&lt;/i&gt; 는 더이상 검색하지 않는 것으로 수정했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개선사항 2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 어쨌든 코* 검색이 오래걸려서&lt;br /&gt;이걸 해결 해야만 했다.&lt;/p&gt;
&lt;h1&gt;PostgreSQL 문자열 검색 속도 높이기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(실패) Full Text Search&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 지금 필요한 검색보다 조금 오버스펙이다.&lt;br /&gt;satisfies, satisfy 요런 단어의 형태 변화까지 잡아낼 수 있는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postgreSQL은 Full Text Search 를 위해,&lt;br /&gt;2가지 자료형을 제공한다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;tsvector&lt;/li&gt;
&lt;li&gt;tsquery&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(실패) tsvector 컬럼 만들기&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;ALTER TABLE user_table ADD COLUMN nickname_ts tsvector;

ALTER TABLE user_table ADD COLUMN nickname_ts tsvector
    GENERATED ALWAYS AS (to_tsvector('simple', nickname)) STORED;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 테스트 디비에 위 쿼리를 날려봤는데, 한세월이 걸렸다.&lt;br /&gt;운영중인 디비에 적용시키기에는 너무 오래 걸리는 쿼리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만드는데 오래걸리는걸로 미루어보아,&lt;br /&gt;row가 새로 생기거나, 유저가 닉네임을 변경해서 update 가 될때도 엄청 느릴거라는 판단을 했다&lt;br /&gt;(simple은 형태소 없이 진행하는거)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(실패) tsvector 인덱스 만들기&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;CREATE INDEX ON user_table USING GIN (to_tsvector('simple', nickname));

SELECT *
FROM   user_table
WHERE  to_tsvector('simple', nickname) @@ to_tsquery('simple', 'a');

select to_tsvector('simple','A_paper') 
# 결과 -&amp;gt; 'a':1 'paper':2

select to_tsquery('simple','A_paper')
# 결과 -&amp;gt; 'a' &amp;lt;-&amp;gt; 'paper'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 해보고 해결 한 줄 알았다.&lt;br /&gt;그런데 'a'가 포함된 닉네임은 실제로는 몇만개인데 반해,&lt;br /&gt;위 to_tsquery 로 a를 검색했을때는 몇백개만 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 매 문자마다 잘려지는게 아니라, 뭔가 알아서 잘려졌다.&lt;br /&gt;그래서 특히 한글일때 원하는 결과가 안나왔다.&lt;/p&gt;
&lt;h1&gt;(성공) pg_trgm 익스텐션&lt;/h1&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;CREATE EXTENSION pg_trgm;
CREATE INDEX user_table ON user_table USING gin (nickname gin_trgm_ops);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익스텐션 깔고, gin 인덱스 만드니까,&lt;br /&gt;실행시간 2초대에서, 1ms 로 줄어드는 기적같은 일이 벌어졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ㄴㄴㅇㄹㄴ.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5KwFk/btsDBxFEQe4/FgjQz3mkn0fWWbm2u7rv40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5KwFk/btsDBxFEQe4/FgjQz3mkn0fWWbm2u7rv40/img.png&quot; data-alt=&quot;디비 이미지 세션 수가 감소했음..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5KwFk/btsDBxFEQe4/FgjQz3mkn0fWWbm2u7rv40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5KwFk%2FbtsDBxFEQe4%2FFgjQz3mkn0fWWbm2u7rv40%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;2870&quot; height=&quot;506&quot; data-filename=&quot;ㄴㄴㅇㄹㄴ.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;506&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;tri 알고리즘은 알고리즘 책 속에만 존재하는 줄 알았는데,&lt;br /&gt;이게 이런 효자같은 녀석이었다니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 pg 익스텐션들이 해답이었던 경우가 많았군&lt;br /&gt;pg_repack도 요긴하게 썼었는데 말이지..&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;text search 관련 함수 공식문서 : &lt;a href=&quot;https://www.postgresql.org/docs/current/functions-textsearch.html&quot;&gt;https://www.postgresql.org/docs/current/functions-textsearch.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend</category>
      <category>full text search</category>
      <category>pg_trgm</category>
      <category>PostgreSQL</category>
      <category>Tri</category>
      <category>문자열검색</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/50</guid>
      <comments>https://jo5ham.tistory.com/50#entry50comment</comments>
      <pubDate>Wed, 17 Jan 2024 21:38:36 +0900</pubDate>
    </item>
    <item>
      <title>Cannot read properties of undefined (reading 'callHandler')</title>
      <link>https://jo5ham.tistory.com/49</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;1192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6a87x/btsgwbM3BCL/kb3HiKWVXOUv3ZT93TXUbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6a87x/btsgwbM3BCL/kb3HiKWVXOUv3ZT93TXUbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6a87x/btsgwbM3BCL/kb3HiKWVXOUv3ZT93TXUbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6a87x%2FbtsgwbM3BCL%2Fkb3HiKWVXOUv3ZT93TXUbk%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;1940&quot; height=&quot;1192&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;1192&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;flutter inappwebview 라이브러리 공식 문서를 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flutterInAppWebViewPlatformReady 라는 리스너를 달아서&lt;br /&gt;플러터 &amp;lt;-&amp;gt; web 사이에 handler 을 불러올 수 있을때..!&amp;nbsp;&lt;br /&gt;그때가 되었을 때에야..!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flutter_inappwebview.callHandler() 을 호출하라고&amp;nbsp;&lt;br /&gt;명시해두었다.&lt;/p&gt;
&lt;pre id=&quot;code_1684476914648&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener(&quot;flutterInAppWebViewPlatformReady&quot;, function(event) {
   const args = [1, true, ['bar', 5], {foo: 'baz'}];
   window.flutter_inappwebview.callHandler('myHandlerName', ...args);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684477009823&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;Javascript WebView Communication | InAppWebView&quot; data-og-description=&quot;You can communicate with JavaScript and vice-versa. There 3 ways to communicate with JavaScript:&quot; data-og-host=&quot;inappwebview.dev:443&quot; data-og-source-url=&quot;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication/&quot; data-og-url=&quot;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inappwebview.dev/docs/5.x.x/webview/javascript/communication/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Javascript WebView Communication | InAppWebView&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;You can communicate with JavaScript and vice-versa. There 3 ways to communicate with JavaScript:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inappwebview.dev:443&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 나는 이 말을 어기고&lt;br /&gt;그냥 callHandler 을 호출하다가&lt;br /&gt;아래 오류를 만나게 되었는데,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Cannot read properties of undefined (reading 'callHandler')&lt;/blockquote&gt;
&lt;pre id=&quot;code_1684477103408&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener(&quot;flutterInAppWebViewPlatformReady&quot;, function(event) {
	console.log(&quot;heyheye!!!!&quot;)
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드를 짜서&lt;br /&gt;아무리 flutterInAppWebViewPlatformReady 이 이벤트가 발생하기를 기다려도&lt;br /&gt;발생하지 않았기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그냥 웹 키고 좀 기다렸다가는&lt;br /&gt;핸들러가 동작했기 때문..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끙.. 이게맞나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 해야할까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 flutterInAppWebViewPlatformReady 이벤트는&lt;br /&gt;끝끝내 발생하지 않았을까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드속 수많은 useEffect 안에서 저 이벤트 리스너가 동작하기를 기다려봐도..흑흑&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>callhandler</category>
      <category>Flutter</category>
      <category>flutter_inappwebview</category>
      <category>inappwebview</category>
      <category>React</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/49</guid>
      <comments>https://jo5ham.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 19 May 2023 15:29:02 +0900</pubDate>
    </item>
    <item>
      <title>Automatic1111 에 컨트리뷰트 하기</title>
      <link>https://jo5ham.tistory.com/48</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2vKim/btr5ZktnodL/jsnCwXQ6yZHlkEoXrY02C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2vKim/btr5ZktnodL/jsnCwXQ6yZHlkEoXrY02C1/img.png&quot; data-alt=&quot;는 사실 오타수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2vKim/btr5ZktnodL/jsnCwXQ6yZHlkEoXrY02C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2vKim%2Fbtr5ZktnodL%2FjsnCwXQ6yZHlkEoXrY02C1%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;1232&quot; height=&quot;698&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;는 사실 오타수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/8824#event-8844922068&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/8824#event-8844922068&lt;/a&gt;&lt;/p&gt;</description>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/48</guid>
      <comments>https://jo5ham.tistory.com/48#entry48comment</comments>
      <pubDate>Mon, 27 Mar 2023 17:38:14 +0900</pubDate>
    </item>
    <item>
      <title>Stable Diffusion : Dreambooth로 무료 셀프 AI Avatar 만들어보기</title>
      <link>https://jo5ham.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 사진들은 모두 AI가 만든 사진입니닷&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjvM7L/btrZ78SS8ol/G4YmInRL1gLVJMNJsTiDAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjvM7L/btrZ78SS8ol/G4YmInRL1gLVJMNJsTiDAK/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjvM7L/btrZ78SS8ol/G4YmInRL1gLVJMNJsTiDAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjvM7L%2FbtrZ78SS8ol%2FG4YmInRL1gLVJMNJsTiDAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6HHU3/btr0fJ4Emhn/WmB61AfXHHFkBZYt6N0UOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6HHU3/btr0fJ4Emhn/WmB61AfXHHFkBZYt6N0UOk/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6HHU3/btr0fJ4Emhn/WmB61AfXHHFkBZYt6N0UOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6HHU3%2Fbtr0fJ4Emhn%2FWmB61AfXHHFkBZYt6N0UOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRSchS/btr0aQ40PFZ/KxlNkCgRfoznhqJkBW0jMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRSchS/btr0aQ40PFZ/KxlNkCgRfoznhqJkBW0jMk/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRSchS/btr0aQ40PFZ/KxlNkCgRfoznhqJkBW0jMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRSchS%2Fbtr0aQ40PFZ%2FKxlNkCgRfoznhqJkBW0jMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKGcru/btrZ9MBPAws/i69zunkjaloLkIynvstl4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKGcru/btrZ9MBPAws/i69zunkjaloLkIynvstl4K/img.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKGcru/btrZ9MBPAws/i69zunkjaloLkIynvstl4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKGcru%2FbtrZ9MBPAws%2Fi69zunkjaloLkIynvstl4K%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;512&quot; height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1M2T/btr0fJQ7dKu/CY225OLpreKua6BiK3GhtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1M2T/btr0fJQ7dKu/CY225OLpreKua6BiK3GhtK/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-widthpercent=&quot;33.34&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-top: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1M2T/btr0fJQ7dKu/CY225OLpreKua6BiK3GhtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1M2T%2Fbtr0fJQ7dKu%2FCY225OLpreKua6BiK3GhtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 학습시킬 내 사진을 준비합니다&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYyyMm/btrZ86mWToS/1ni6Tjihhhdh9PNVr46Cb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYyyMm/btrZ86mWToS/1ni6Tjihhhdh9PNVr46Cb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYyyMm/btrZ86mWToS/1ni6Tjihhhdh9PNVr46Cb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYyyMm%2FbtrZ86mWToS%2F1ni6Tjihhhdh9PNVr46Cb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1063&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1063&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 데이터의 개수는 6개 ~ 16개 까지 해보았는데 다 퀄이 괜찮았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼굴 이미지를 학습에 사용하기 위해서&lt;br /&gt;512*512px로 사이즈를 수정하고,&lt;br /&gt;파일명은 고유한 변수 + 넘버링 으로 했습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g. &quot;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt; (1)&quot; (이세상에 존재하지 않는 단어인것이 좋음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이즈 조정 및 네이밍 변경은&lt;br /&gt;아래 Birme 사이트에서 간편하게 가능합니다&lt;/p&gt;
&lt;figure id=&quot;og_1676958748369&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;BIRME - Bulk Image Resizing Made Easy 2.0 (Online &amp;amp; Free)&quot; data-og-description=&quot;About Birme New features in version 2 NEW* Added support for WebP format Now you can adjust the focal point of each photo individualy Auto load previous settings you used Auto focal point detection Renaming file names in bulk Save resized images as files i&quot; data-og-host=&quot;www.birme.net&quot; data-og-source-url=&quot;https://www.birme.net/&quot; data-og-url=&quot;https://www.birme.net/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iingw/hyRHuPjjom/CXkHZFR3vVgYXuKOKzyHnK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.birme.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.birme.net/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iingw/hyRHuPjjom/CXkHZFR3vVgYXuKOKzyHnK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;BIRME - Bulk Image Resizing Made Easy 2.0 (Online &amp;amp; Free)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;About Birme New features in version 2 NEW* Added support for WebP format Now you can adjust the focal point of each photo individualy Auto load previous settings you used Auto focal point detection Renaming file names in bulk Save resized images as files i&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.birme.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;h2 data-ke-size=&quot;size26&quot;&gt;아래 colab 노트북을 열어줍니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676958652259&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;DreamBooth_Stable_Diffusion_SDA.ipynb&quot; data-og-description=&quot;Run, share, and edit Python notebooks&quot; data-og-host=&quot;colab.research.google.com&quot; data-og-source-url=&quot;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&quot; data-og-url=&quot;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tzSaZ/hyRHuPjeGw/kdkkWFKuPEwrNBMXSgYjX0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://colab.research.google.com/github/sagiodev/stablediffusion_webui/blob/master/DreamBooth_Stable_Diffusion_SDA.ipynb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tzSaZ/hyRHuPjeGw/kdkkWFKuPEwrNBMXSgYjX0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&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;DreamBooth_Stable_Diffusion_SDA.ipynb&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Run, share, and edit Python notebooks&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;colab.research.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 모델 학습시키기&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 실행 블록입니다.&amp;nbsp;&lt;br /&gt;변경할 것은 두가지 입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zwx를 본인의 고유한 변수 (e.g. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt;) 으로 변경해주세요&lt;br /&gt;toy를 person으로 변경해주세요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 실행하면 중간에 준비한 이미지 파일들을 업로드 하고,&lt;br /&gt;약 20분 정도 소요되며 &lt;br /&gt;이 동안 모델에 제 얼굴이 학습됩니다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3712&quot; data-origin-height=&quot;1346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lHSeW/btr0dmiif10/zyCKlpvnzhhVQDtQkckeeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lHSeW/btr0dmiif10/zyCKlpvnzhhVQDtQkckeeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lHSeW/btr0dmiif10/zyCKlpvnzhhVQDtQkckeeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlHSeW%2Fbtr0dmiif10%2FzyCKlpvnzhhVQDtQkckeeK%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;3712&quot; height=&quot;1346&quot; data-origin-width=&quot;3712&quot; data-origin-height=&quot;1346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2260&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp7kjZ/btrZ84CFCgl/9sdJsm9Py6M3SAqTzFuJpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp7kjZ/btrZ84CFCgl/9sdJsm9Py6M3SAqTzFuJpK/img.png&quot; data-alt=&quot;다 학습이 되고 나온 샘플 이미지들. 아직까지는 나를 그렇게 닮은지 모르겠지만..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp7kjZ/btrZ84CFCgl/9sdJsm9Py6M3SAqTzFuJpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp7kjZ%2FbtrZ84CFCgl%2F9sdJsm9Py6M3SAqTzFuJpK%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;2260&quot; height=&quot;572&quot; data-origin-width=&quot;2260&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다 학습이 되고 나온 샘플 이미지들. 아직까지는 나를 그렇게 닮은지 모르겠지만..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 모델에 prompt 넣어서 text-to-image 생성해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고유한 변수 &quot;johamcarat&quot; 을 사용하여 모델에 prompt를 넣어보겠습니다&lt;br /&gt;(변수를 사용하지 않아도 이미지는 생성됩니다만,,&amp;nbsp;&lt;br /&gt;제 자신의 아바타를 보기 위해서는 꼭 고유한 변수를 사용해주어야 합니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e.g. portrait of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt;, 8k, high quality, k-pop idol&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDcGRk/btrZ7FXIgdy/n0DNcZGN0HiAlcvqYYuq9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDcGRk/btrZ7FXIgdy/n0DNcZGN0HiAlcvqYYuq9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDcGRk/btrZ7FXIgdy/n0DNcZGN0HiAlcvqYYuq9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDcGRk%2FbtrZ7FXIgdy%2Fn0DNcZGN0HiAlcvqYYuq9K%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;512&quot; height=&quot;512&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 더 나은 결과물을 얻기 위한 prompt 찾아 헤메기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prompt 에 따라 결과물이 천차만별입니다&lt;br /&gt;negative prompt 도 잘 사용하시면 원하시는 결과물에 더 가까워 지실거에요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇가지 맘에 들었던 prompt 공유합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- pencil sketch, portrait of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt; woman, high quality, Gray-scale&lt;br /&gt;- portrait of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt; woman, high quality, watercolor, detailed&lt;br /&gt;- portrait of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt; person, 8k, high quality&lt;br /&gt;- pencil sketch of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;johamcarat&lt;/span&gt; woman, joham, joham woman, inpired by greg rutkowski, digital art by artgemGuidance&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stable-diffusion-art.com/dreambooth/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stable-diffusion-art.com/dreambooth/&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;</description>
      <category>aiavatar</category>
      <category>Avatar</category>
      <category>COLAB</category>
      <category>diffusion</category>
      <category>dreambooth</category>
      <category>prompt</category>
      <category>stable</category>
      <category>StableDiffusion</category>
      <category>Text-to-Image</category>
      <category>texttoimage</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/47</guid>
      <comments>https://jo5ham.tistory.com/47#entry47comment</comments>
      <pubDate>Tue, 21 Feb 2023 15:09:33 +0900</pubDate>
    </item>
    <item>
      <title>서버 2대중 1대만 특정 태스크를 (알람) 하도록 하는 법</title>
      <link>https://jo5ham.tistory.com/46</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1차 시도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master_server db 를 만들어서&lt;br /&gt;마스터가 될 ip 정보를 넣어두었다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W5A8V/btrL9jd98sZ/HI9V7HMyKYOs7wzLVgcw6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W5A8V/btrL9jd98sZ/HI9V7HMyKYOs7wzLVgcw6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W5A8V/btrL9jd98sZ/HI9V7HMyKYOs7wzLVgcw6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW5A8V%2FbtrL9jd98sZ%2FHI9V7HMyKYOs7wzLVgcw6K%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;910&quot; height=&quot;214&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 서버들은 1분마다&lt;br /&gt;아래 체크를 해서&amp;nbsp;&lt;br /&gt;본인이 마스터인지 확인을 했다&lt;/p&gt;
&lt;pre id=&quot;code_1663131924396&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Transactional(isolation = Isolation.SERIALIZABLE)
    fun checkIfMasterServer(): Boolean {

        val masterServerEntity = masterServerRepository.findByIdOrNull(1) ?: MasterServerEntity(1)

        val expireAt = masterServerEntity.expireAt
        if (expireAt?.isBefore(LocalDateTime.now()) != false) {
            masterServerEntity.ip = InetAddress.getLocalHost().hostAddress
            masterServerEntity.expireAt = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).plusMinutes(2)

            masterServerRepository.save(masterServerEntity)
            println(&quot;오 마스터 자리 있당 : ${InetAddress.getLocalHost().hostAddress}&quot;)
        } else {
            //todo
            println(&quot;이미 자리를 먹혔군&quot;)
        }

        val nowMasterServer = masterServerRepository.findByIdOrNull(1)

        return nowMasterServer?.ip == InetAddress.getLocalHost().hostAddress
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 2대의 서버가 자꾸 &lt;br /&gt;본인을 마스터라고 우기는 현상 발생&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYG47Y/btrL3FcttVa/TyzcPJGJDfSnmMjljxk4VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYG47Y/btrL3FcttVa/TyzcPJGJDfSnmMjljxk4VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYG47Y/btrL3FcttVa/TyzcPJGJDfSnmMjljxk4VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYG47Y%2FbtrL3FcttVa%2FTyzcPJGJDfSnmMjljxk4VK%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;1430&quot; height=&quot;420&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은&lt;br /&gt;두 서버가 master_server 의 첫번째 줄이 비어있는걸 동시에 보고&lt;br /&gt;본인들의 ip 를 넣은뒤&lt;br /&gt;본인들이 마스터라고 자축한것;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 서버가 동시에 첫번째 줄이 비어있다고 보는 현상을 막기 위해&lt;br /&gt;isolation 레벨을 가장 높은 Serializable 로 바꿨으나&lt;br /&gt;이 현상이 계속 되었다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2차 시도&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9Qx50/btrL43cOU0M/xvPb6bVbaNoOCokAvxngm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9Qx50/btrL43cOU0M/xvPb6bVbaNoOCokAvxngm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9Qx50/btrL43cOU0M/xvPb6bVbaNoOCokAvxngm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9Qx50%2FbtrL43cOU0M%2FxvPb6bVbaNoOCokAvxngm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;254&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 서버들이 본인 ip 와&lt;br /&gt;본인이 살아있음을 알리기 위해&lt;br /&gt;health_check_at 컬럼 값을 당시 시간으로 1분마다 업데이트 하도록 했다&lt;br /&gt;(master_server_health 테이블)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 기존 master_server 테이블에는&lt;br /&gt;health_check_at 봤을때 살아있는 애들 중에 &lt;br /&gt;(최근 2분안의 값인 경우)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ip 로 정렬해서 하나 뽑아서 값을 넣도록 했다&lt;br /&gt;이러면 여러 서버들이 ip를 넣어도 다 같은걸 넣겠찌?!&lt;/p&gt;
&lt;pre id=&quot;code_1663133724642&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    fun checkIfMasterServer(): Boolean {
        return masterServerRepository.findByIdOrNull(1)?.ip == InetAddress.getLocalHost().hostAddress
    }
    
    
    @Scheduled(cron = &quot;0 * * * * *&quot;)
    fun serverHealthCheck() {
        val ip = InetAddress.getLocalHost().hostAddress
        val entity = masterServerHealthCheckRepository.findByIdOrNull(ip)
            ?: MasterServerHealthCheckEntity(ip)
        entity.healthCheckAt = LocalDateTime.now()
        masterServerHealthCheckRepository.save(entity)

        val masterIp =
            masterServerHealthCheckRepository.findAllByHealthCheckAtAfterOrderByIp(LocalDateTime.now().minusMinutes(3))
                .first()
                .ip

        //todo
        println(&quot;제가 찾아낸 masterIp 는 바로 : $masterIp&quot;)
        val masterEntity = masterServerRepository.findByIdOrNull(1)
            ?: MasterServerEntity(1)
        masterEntity.ip = masterIp
        masterServerRepository.save(masterEntity)

        val expiredIps = masterServerHealthCheckRepository.findAllByHealthCheckAtBeforeOrderByIp(
            LocalDateTime.now().minusMinutes(10)
        ).map { it.ip }
        try {
            masterServerHealthCheckRepository.deleteAllById(expiredIps)
        } catch (_: Exception) {
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 모두가...&lt;br /&gt;입을 모아 동일한 마스터를 찾기 시작...&lt;br /&gt;문제 해결&lt;br /&gt;꺆&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNWfEX/btrL9bHwDDO/4QXXEk8OBDFsfaaNlceLJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNWfEX/btrL9bHwDDO/4QXXEk8OBDFsfaaNlceLJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNWfEX/btrL9bHwDDO/4QXXEk8OBDFsfaaNlceLJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNWfEX%2FbtrL9bHwDDO%2F4QXXEk8OBDFsfaaNlceLJ0%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;1456&quot; height=&quot;742&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Backend</category>
      <category>Health Check</category>
      <category>JPA</category>
      <category>Master</category>
      <category>master-server</category>
      <category>server</category>
      <category>spring boot</category>
      <category>마스터</category>
      <category>마스터서버</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/46</guid>
      <comments>https://jo5ham.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 14 Sep 2022 14:48:47 +0900</pubDate>
    </item>
    <item>
      <title>[에러해결] Unexpected error occurred in scheduled task</title>
      <link>https://jo5ham.tistory.com/45</link>
      <description>&lt;pre id=&quot;code_1663045100235&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Unexpected error occurred in scheduled task
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 스프링부트 + JPA 를 사용해서&amp;nbsp;&lt;br /&gt;백엔드를 구축 중이었당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 주기적인 알람 기능을 구현하면서&lt;br /&gt;@Scheduled 어노테이션을 사용하게 되었고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자연스럽게 @Transactional 어노테이션이 붙어있던 트랜잭션에&lt;br /&gt;@Scheduled 어노테이션을 붙여서 사용하게 되었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Unexpected error occurred in scheduled task 에러&lt;br /&gt;발생..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링을 하다가 발견한&lt;br /&gt;갓영한님의 목소리&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbSyST/btrLTV7fWJp/tMa2KHHTxW4FWPITMsfmXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbSyST/btrLTV7fWJp/tMa2KHHTxW4FWPITMsfmXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbSyST/btrLTV7fWJp/tMa2KHHTxW4FWPITMsfmXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbSyST%2FbtrLTV7fWJp%2FtMa2KHHTxW4FWPITMsfmXK%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;1228&quot; height=&quot;436&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니..!!!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몰랐어요..!!!!!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 @Scheduled 와 @Transactional 어노테이션을 분리해야 하는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 일단&lt;br /&gt;@Transactional 은 스프링에서 제공해주는 AOP functionality 라고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 요 어노테이션이 붙은 메소드는&lt;br /&gt;다른 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;@Transactional&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation is processed. It create proxy which store reference to original bean. Original bean is replaced to proxy in application context.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미래의 내가 이해하겠지..(?)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 해결법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 메소드를 동시에 두지 않고&lt;br /&gt;한 메소드에 한 어노테이션만 두자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 늑낌으루~&lt;/p&gt;
&lt;pre id=&quot;code_1663046210341&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class UserServiceImpl implements UserService {

    @Override
    @Transactional
    public void doSomething() {

    }
}

@Service
public class UserServiceScheduler {

    @Inject
    private UserService service;

    @Scheduled(fixedRateString = &quot;${somestring}&quot;,initialDelayString = &quot;${anotherstring}&quot;)
    public void doSomething() {
         service.doSomething();
    }
}&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;내 케이스에서는 사실&lt;br /&gt;@Transactional 과 @Scheduled 를 함께 쓸 이유가 딱히 없었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(같이 썼던 메소드가 10개라면 그중 8개 정도는 Transactional이 불필요 했던듯)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 그냥 db의 데이터들을 읽어오는 메소드 들이었기 때무네...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 그냥 내가&lt;br /&gt;Transactional 어노테이션이 좋다던데~&lt;br /&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;JPA repository 의 save 메소드에는&lt;br /&gt;내부적으로 @Transactional 이 붙어있다고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그 외부 메소드에&lt;br /&gt;@Transactional 을 쓰려면&lt;br /&gt;트랜잭션을 상속받는 방식중에 뭘 쓸지를 골라야 한다는것!!&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;br /&gt;Transactional 어노테이션을 다 제거해버렸다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐음 이게 맞나..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 Transactional 어노테이션을 붙였던 이유는&lt;br /&gt;알람을 보내는 로직에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 알람을 보냈다고 db에 알람 기록한 시점도 저장하고 (repository.save())&lt;br /&gt;2. firebase 알람 시스템을 이용해서 알람을 보내는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 2개 로직이&lt;br /&gt;둘중에 실패했을때 롤백되기를 원했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 2개 로직을 하나의 함수로 묶어서&lt;br /&gt;그 위에 Transactional을 달았는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;why..&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 나같이 @Scheduled @Transactional 같이 쓰면 웨 않되요? 하는 스택오버플로우&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/45355601/why-scheduled-annotation-doesnt-work-with-transaction-annotation-spring-boot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/45355601/why-scheduled-annotation-doesnt-work-with-transaction-annotation-spring-boot&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 갓영한님의 답변이 담긴 인프런 질의응답&lt;br /&gt;&lt;a href=&quot;https://www.inflearn.com/questions/297130&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/questions/297130&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. JPA의 save 에는 이미 @Transactional이 달려있는데, 외부메소드에도 @Transactional 있을때 해결책 3개 제시해주는 포스팅&lt;br /&gt;&lt;a href=&quot;https://ws-pace.tistory.com/138&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ws-pace.tistory.com/138&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error</category>
      <category>Error</category>
      <category>Java</category>
      <category>JPA</category>
      <category>Kotlin</category>
      <category>scheduled</category>
      <category>spring</category>
      <category>Transactional</category>
      <category>Unexpected error occurred in scheduled task</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/45</guid>
      <comments>https://jo5ham.tistory.com/45#entry45comment</comments>
      <pubDate>Tue, 13 Sep 2022 14:18:55 +0900</pubDate>
    </item>
    <item>
      <title>Read-Only 함수에 @Transactional 을 붙여야 하는가?</title>
      <link>https://jo5ham.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링부트 + JPA 를 쓰다보면&lt;br /&gt;@Transactional 어노테이션을 자주 사용하게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 로직이 동작하면서&lt;br /&gt;DB 값도 바꿔야 하는 상황일때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 로직이 실패하면&amp;nbsp;&lt;br /&gt;DB까지 모두 롤백시킬 심산으로 &lt;br /&gt;@Transactional 어노테이션을 활용하고는 했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 어느날&lt;br /&gt;내가 Transactional 어노테이션을 남용하고 있는건 아닐까..?&lt;/p&gt;
&lt;pre id=&quot;code_1663041305431&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional(readOnly = true)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 읽기만 할건데..&lt;br /&gt;롤백 될일도 없는데..&lt;br /&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;알고보니 Spring JPA repository base class 가&lt;br /&gt;애초에 모든 메소드들을 read-only transaction 으로 생각한다고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 코드는 필요 없는것!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JPA Buddy&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JPA 최적화 관련해서&lt;br /&gt;글들을 보던중&lt;br /&gt;JPA Buddy 라는 intelliJ에서 쓸 수 있는&lt;br /&gt;플러그인을 알게 되었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 제대로 활용은 못해봤지만&lt;br /&gt;entity 클래스도 자동으로 생성해주고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각&amp;nbsp; entity 에 연결된 repository 도 알아서 잘 보여준다..!&amp;nbsp;&lt;br /&gt;싱기방기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어려운 JPA의 세계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA 관련 책 추천받습니다&lt;/p&gt;</description>
      <category>Backend</category>
      <category>IntelliJ</category>
      <category>JPA</category>
      <category>jpabuddy</category>
      <category>read-only</category>
      <category>readonly</category>
      <category>spring</category>
      <category>SpringBoot</category>
      <category>Transactional</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/44</guid>
      <comments>https://jo5ham.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 13 Sep 2022 13:08:22 +0900</pubDate>
    </item>
    <item>
      <title>DEM? DSM? dxf? 데이터에 대해 알아보져</title>
      <link>https://jo5ham.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;DEM 이란 무엇인가&lt;br /&gt;Digital elevation model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSM 이란 무엇인가&lt;br /&gt;Digital surface model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;아래 이미지는 위키피디아에서 가져온, 화성의 Tithonium Chasma (? 무슨 협곡이라고 함) DEM 데이터를 가지고&lt;br /&gt;3D렌더링을 한 결과물이라고 한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3ZY7U/btrpU0nkVLo/6phEQ7yeK82UL06aK43Sk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3ZY7U/btrpU0nkVLo/6phEQ7yeK82UL06aK43Sk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3ZY7U/btrpU0nkVLo/6phEQ7yeK82UL06aK43Sk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3ZY7U%2FbtrpU0nkVLo%2F6phEQ7yeK82UL06aK43Sk0%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;290&quot; height=&quot;221&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DEM 데이터는 말그대로 elevation 데이터이기 때문에, &lt;br /&gt;이를 이용해서는 3차원 시각화나, 지형의 평균 경사도 등을 파악해 볼 수 있다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DEM 과 DSM 은 어떤 차이인가?&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLVrjU/btrpOxUJOts/DAtRxt2Ubvk6f3jzBJYoEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLVrjU/btrpOxUJOts/DAtRxt2Ubvk6f3jzBJYoEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLVrjU/btrpOxUJOts/DAtRxt2Ubvk6f3jzBJYoEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLVrjU%2FbtrpOxUJOts%2FDAtRxt2Ubvk6f3jzBJYoEK%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;380&quot; height=&quot;197&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DEM 은 bare earth 를 표현한다고 한다. (인공지물과 식생을 제외)&lt;br /&gt;DSM 은 인공지물과 식생을 포함한 지구 표면을 표현한 데이터이다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;AW3D30&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AW3D30 은 전세계 30m급 DSM 을 무료로 제공하고 있는 데이터셋이다&lt;br /&gt;AW3D30 을 풀어서 쓰자면 ALOS World 3D - 30m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ALOS 를 또 풀어서 쓰자면 Advanced Land Observing Satellite-3&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;ALOS-3 소개 링크&quot; href=&quot;https://www.eorc.jaxa.jp/ALOS/en/index_e.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.eorc.jaxa.jp/ALOS/en/index_e.htm&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 ALOS-3 위성이 찍어온 DSM 데이터를&lt;br /&gt;아래 링크에서 무려 무료로 받아 사용할 수 있다고 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d_e.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d_e.htm&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1641371342662&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;ALOS｜ALOS@EORCホームページ&quot; data-og-description=&quot;ALOSの各センサで観測された画像と解析事例を公開しています。&quot; data-og-host=&quot;www.eorc.jaxa.jp&quot; data-og-source-url=&quot;https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d_e.htm&quot; data-og-url=&quot;https://www.eorc.jaxa.jp/ALOS/index_e.htm&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d_e.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.eorc.jaxa.jp/ALOS/en/dataset/aw3d_e.htm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ALOS｜ALOS@EORCホームページ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ALOSの各センサで観測された画像と解析事例を公開しています。&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.eorc.jaxa.jp&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;dxf ?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dxf 는 오토캐드를 포함한 CAD 프로그램들이 캐드 파일을 -&amp;gt; 가독성 있는 텍스트 파일로 변환하는 파일 포맷이다&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발log</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/43</guid>
      <comments>https://jo5ham.tistory.com/43#entry43comment</comments>
      <pubDate>Wed, 5 Jan 2022 17:20:17 +0900</pubDate>
    </item>
    <item>
      <title>react 프로젝트 clone 후 작업</title>
      <link>https://jo5ham.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;clone 해온 후 프로젝트 directory 에 들어가서 아래 명령어를 통해 초기 작업을 해주었다&lt;/p&gt;
&lt;pre id=&quot;code_1640936439603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;npm install을 하면 package.json에 있는 dependencies 필드 정보를 기반으로 직접 생성한다. &lt;br /&gt;node_modules 디렉토리를 공유하면 데이터 낭비가 심하기 때문이다.&lt;br /&gt;&lt;br /&gt;package-lock.json 은 node_modules 디렉토리 내부의 차이발생을 방지한다. &lt;br /&gt;즉, 특정 버전의 패키지들을 동일하게 설치해준다. &lt;br /&gt;패키지를 공유할 때 정확히 똑같은 패키지 버전을 가져야 잘 작동하는 패키지도 있기 때문이다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNW7IR/btrptgxHcFy/S6Qvzpf4y754XBHkHsP9kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNW7IR/btrptgxHcFy/S6Qvzpf4y754XBHkHsP9kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNW7IR/btrptgxHcFy/S6Qvzpf4y754XBHkHsP9kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNW7IR%2FbtrptgxHcFy%2FS6Qvzpf4y754XBHkHsP9kK%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;950&quot; height=&quot;488&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm install 후 뜨는 아래 메세지들을 알아볼 예정이다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1 package is looking for funding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 1 package is looking for funding 이라는 메세지는,&amp;nbsp;&lt;br /&gt;말 그대로 후원을 바라고 있다는 뜻..&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;When you run&amp;nbsp;npm fund&amp;nbsp;it will list all the modules and packages you have installed that were created by companies or organizations that need&amp;nbsp;money&amp;nbsp;for their IT projects.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈이 없는 나는 아래와 같이 --no-fund 옵션을 붙여 fund 원한다는 메세지가 뜨지 않도록 할 수 있었다&lt;/p&gt;
&lt;pre id=&quot;code_1640936803965&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install --no-fund&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;npm audit&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm audit 기능은 사용하고 있는 라이브러리들의 취약점을 검색해서 알려주는 것이라고 한다.&lt;br /&gt;취약점 검사는 NSP (node security platform) 을 사용해서 진행된다고 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm audit fix 기능을 사용하면,&lt;br /&gt;취약점이 있는 버전을 보다 안전하고, 호환 가능한 버전으로 자동 업데이트를 수행해 준다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm audit fix --force 요렇게 force 옵션까지 넣어서 수행하면,&lt;br /&gt;minor 버전만 수정하는 것이 아닌, major 버전까지도 수정할 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쪼다인 나는 force 옵션 없이 npm audit fix만 진행해 보았다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 : 29&amp;nbsp;vulnerabilities&amp;nbsp;(2&amp;nbsp;low,&amp;nbsp;20&amp;nbsp;moderate,&amp;nbsp;2&amp;nbsp;high,&amp;nbsp;5&amp;nbsp;critical)&lt;br /&gt;npm audit fix 후 : 21&amp;nbsp;vulnerabilities&amp;nbsp;(2&amp;nbsp;low,&amp;nbsp;15&amp;nbsp;moderate,&amp;nbsp;4&amp;nbsp;critical)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업 많으로 완벽하게 해결이 되지는 않았지만,&lt;br /&gt;명령어 한줄로 어느정도 해결이 가능하니,&amp;nbsp;&lt;br /&gt;웬만하면 사용하는게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 구글링을 해보다 보니, npm audit fix 를 수행하다가 오류가 난 케이스 들도 보였기에,,&lt;br /&gt;끙&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reference&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://gatudy.com/npm%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95%EA%B3%BC-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gatudy.com/npm%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95%EA%B3%BC-%EC%9B%90%EB%A6%AC&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;</description>
      <category>개발log</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/42</guid>
      <comments>https://jo5ham.tistory.com/42#entry42comment</comments>
      <pubDate>Fri, 31 Dec 2021 16:54:08 +0900</pubDate>
    </item>
    <item>
      <title>nginx 버전이 노출되어 있으면 위험할 수 있다구?</title>
      <link>https://jo5ham.tistory.com/41</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v9iZX/btroZFk8OAM/ZNqnc1687kHdDctBLFUBiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v9iZX/btroZFk8OAM/ZNqnc1687kHdDctBLFUBiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v9iZX/btroZFk8OAM/ZNqnc1687kHdDctBLFUBiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv9iZX%2FbtroZFk8OAM%2FZNqnc1687kHdDctBLFUBiK%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;951&quot; height=&quot;367&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 의 버전을 외부 사용자가 확인할 수 있게 노출시켜두면 위험하다!&lt;br /&gt;nginx 버전이 노출되는 대표적인 페이지로는 오류 페이지가 있다&lt;br /&gt;nginx 에서 오류를 응답할때 기본적으로 반환하는 페이지에 버전 정보가 나오게 된다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 왜 위험한가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 의 특정 버전의 취약점이 발견되면 이를 이용한 공격이 너무 쉬워진다.&lt;br /&gt;최소한 내 서버의 nginx 버전이라도 모르게 해야한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 어떻게 대응해야 하는가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 의 기본 오류 페이지가 반환되지 않도록 하거나,&lt;br /&gt;위 기본 오류 페이지에서 버전 정보가 나가지 않도록 조치를 해야 한다&lt;/p&gt;
&lt;pre id=&quot;code_1640611077852&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#nginx.conf
http{
 ...
 server_tokens off;
 ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 옵션을 추가해주면, 아래와 같이 버전정보 없이 오류페이지를 반환한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIKXHj/btro596JT19/TUVpuWA5IQr72b84PlLsy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIKXHj/btro596JT19/TUVpuWA5IQr72b84PlLsy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIKXHj/btro596JT19/TUVpuWA5IQr72b84PlLsy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIKXHj%2Fbtro596JT19%2FTUVpuWA5IQr72b84PlLsy1%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;937&quot; height=&quot;599&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 요정도 대응이면 충분한가?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 nginx 버전 제거 작업을 하면서,&amp;nbsp;&lt;br /&gt;이 외에도 보안적인 관점에서 더 조치해 두어야하는 부분들은 없는지 확인해 보게 되었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 알게된 nginx 보안을 위한 조치 사항은 다음과 같다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Step 1. 미사용 nginx module들 비활성화 시키기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 를 설치하면, 자동적으로 기본 모듈들을 포함시켜서 설치하게 되는데,&lt;br /&gt;불필요한 모듈들이 많아지면 많아질수록 보안에는 취약해 질것이니..&lt;br /&gt;설치 중에 필요한 모듈들 외에는 disable 하도록 설정하기&lt;/p&gt;
&lt;pre id=&quot;code_1640611640071&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ./configure --without-http_autoindex_module
# make
# make install&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Step 2. server_tokens off&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;server_tokens off 를 설정하지 않아서 nginx 버전이 노출되게 두어서 받을 수 있는 공격들을,&lt;br /&gt;information disclousure attack~ 이라고 부른다고 하는군..&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Step 3. 리소스 제한&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dos 공격을 받지 않기 위해서, 리소스 관련 용량 제한등을 해둘 필요성이 있다.&lt;br /&gt;이전에 서버 api 체크 하다가 큰 파일 전송 안되어서 뭬얏! 한 적이 있었는데,&lt;br /&gt;역시 nginx.. 다 계획이 있었구나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client_body_buffer_size&lt;br /&gt;- client_header_buffer_size&lt;br /&gt;- client_max_body_size&lt;br /&gt;- large_client_header_buffers&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Step 4. 안쓰는 HTTP 메소드 제한&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보면 이 방법이 정말 간단하고 보안 효과도 좋을 것 같은데 꼭 도입하는게 좋을 것 같다&lt;/p&gt;
&lt;pre id=&quot;code_1640612210340&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#nginx.conf
location / {
limit_except GET HEAD POST { deny all; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Step 5. 더 많은데 이제 어렵다. Reference에..&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Reference&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- nginx server_tokens doc : &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens&lt;/a&gt;&lt;br /&gt;- hardening nginx : &lt;a href=&quot;https://www.acunetix.com/blog/web-security-zone/hardening-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acunetix.com/blog/web-security-zone/hardening-nginx/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Backend</category>
      <author>!쪼렙조햄</author>
      <guid isPermaLink="true">https://jo5ham.tistory.com/41</guid>
      <comments>https://jo5ham.tistory.com/41#entry41comment</comments>
      <pubDate>Mon, 27 Dec 2021 22:37:57 +0900</pubDate>
    </item>
  </channel>
</rss>