<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발잡부</title>
    <link>https://right1203.tistory.com/</link>
    <description>IT 회사에 데이터 분석, 개발 등을 하고 있습니다.</description>
    <language>ko</language>
    <pubDate>Tue, 21 Jul 2026 13:58:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>훈스비</managingEditor>
    <image>
      <title>개발잡부</title>
      <url>https://tistory1.daumcdn.net/tistory/3173110/attach/749c2b6ce51342a8a4fc857256f661cb</url>
      <link>https://right1203.tistory.com</link>
    </image>
    <item>
      <title>Claude Cowork와 함께 쓰는 프리덴 소파 구매 후기</title>
      <link>https://right1203.tistory.com/44</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;아래 글은 모두 Claude Cowork가 작성한 내용입니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결혼을 하고 신혼집으로 이사를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 집에 발을 들이는 순간부터 시작되는 일이 있다. 바로 가구 쇼핑. 침대 프레임은 어떤 걸로 할까, 소파는 어디서 살까, 고민이 산처럼 쌓이기 시작했다. 인테리어에 크게 관심이 없었던 나도 막상 '내 집'이 생기고 나니 괜히 욕심이 났다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;소파_전체넓게.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjBXGI/dJMcadVre7I/wAKjnMEczn8hEBHiKxgeY1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjBXGI/dJMcadVre7I/wAKjnMEczn8hEBHiKxgeY1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjBXGI/dJMcadVre7I/wAKjnMEczn8hEBHiKxgeY1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjBXGI%2FdJMcadVre7I%2FwAKjnMEczn8hEBHiKxgeY1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;소파_전체넓게.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발품 팔기: 파주부터 광주까지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소파와 침대 프레임을 찾기 위해 정말 많은 곳을 돌아다녔다. 파주 가구 단지, 광주 가구 거리, 백화점 가구 코너, 동네 가구거리까지. 마음에 드는 소파를 발견하면 가격이 예산을 훌쩍 초과했고, 가격이 적당하다 싶으면 디자인이나 품질이 아쉬웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발이 부르트도록 돌아다니면서 느낀 건, 가구는 정말 '보는 눈'이 중요하다는 것. 사진으로 볼 때와 직접 앉아보는 느낌이 전혀 달랐다. 쿠션감, 패브릭의 질감, 실제 공간에 놓았을 때의 크기감... 직접 경험하지 않으면 모르는 것들이 너무 많았다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;드디어 발견한 프리덴 본사 쇼룸 (광주)&lt;/h2&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;프리덴 본사 쇼룸에 들어서자마자 사장님이 호쾌하게 맞아주셨다. 1층은 주로 가죽 소파 위주로 전시되어 있어서 우리 취향과는 조금 달랐지만, 공간 자체가 예쁘게 꾸며져 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 사장님이 &quot;2층도 한번 보세요&quot;라며 안내해 주셨고, 따라올라간 2층에서 완전히 마음을 빼앗겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-39 002.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V1ZDk/dJMcabJ96yG/cKXlSh3ZyBsvKjcaVqvXk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V1ZDk/dJMcabJ96yG/cKXlSh3ZyBsvKjcaVqvXk0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V1ZDk/dJMcabJ96yG/cKXlSh3ZyBsvKjcaVqvXk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV1ZDk%2FdJMcabJ96yG%2FcKXlSh3ZyBsvKjcaVqvXk0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-39 002.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-40 003.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WNQ1V/dJMcabJ96yE/DZYmwBgsUJYW3TgrvKpZYK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WNQ1V/dJMcabJ96yE/DZYmwBgsUJYW3TgrvKpZYK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WNQ1V/dJMcabJ96yE/DZYmwBgsUJYW3TgrvKpZYK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWNQ1V%2FdJMcabJ96yE%2FDZYmwBgsUJYW3TgrvKpZYK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-40 003.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 004.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kgcjr/dJMcabJ96yF/XlOkGoBTRKj0Sjy2WKTkOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kgcjr/dJMcabJ96yF/XlOkGoBTRKj0Sjy2WKTkOk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kgcjr/dJMcabJ96yF/XlOkGoBTRKj0Sjy2WKTkOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKgcjr%2FdJMcabJ96yF%2FXlOkGoBTRKj0Sjy2WKTkOk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 004.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 005.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BHC7u/dJMcafy05uL/osZPKUzi7KsINYKLafgzL1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BHC7u/dJMcafy05uL/osZPKUzi7KsINYKLafgzL1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BHC7u/dJMcafy05uL/osZPKUzi7KsINYKLafgzL1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBHC7u%2FdJMcafy05uL%2FosZPKUzi7KsINYKLafgzL1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 005.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 007.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/corFod/dJMcaaqVIuR/PrJJXWKhVck6QWxLkFhLF0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/corFod/dJMcaaqVIuR/PrJJXWKhVck6QWxLkFhLF0/img.jpg&quot; data-alt=&quot;내가 구매한 &amp;quot;클라우드 소파&amp;quot;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/corFod/dJMcaaqVIuR/PrJJXWKhVck6QWxLkFhLF0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcorFod%2FdJMcaaqVIuR%2FPrJJXWKhVck6QWxLkFhLF0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-41 007.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내가 구매한 &quot;클라우드 소파&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-42 008.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2IaZp/dJMcabJ96yD/f6zwyo9IH8qxRPKUcq2w20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2IaZp/dJMcabJ96yD/f6zwyo9IH8qxRPKUcq2w20/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2IaZp/dJMcabJ96yD/f6zwyo9IH8qxRPKUcq2w20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2IaZp%2FdJMcabJ96yD%2Ff6zwyo9IH8qxRPKUcq2w20%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-42 008.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-42 009.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czopvk/dJMcafy05uO/MgFOwz5b5NlosTj5T1Ke40/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czopvk/dJMcafy05uO/MgFOwz5b5NlosTj5T1Ke40/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czopvk/dJMcafy05uO/MgFOwz5b5NlosTj5T1Ke40/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fczopvk%2FdJMcafy05uO%2FMgFOwz5b5NlosTj5T1Ke40%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-16-30-42 009.jpeg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가격이 나가는 이유가 있다 &amp;mdash; 공장 투어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2층 소파들은 상당히 마음에 들었지만, 가격이 저렴한 편은 아니었다. 잠시 망설이는 기색을 보이자 사장님께서 직접 공장으로 데려가주셨다.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구매 결정, 그리고 배송&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 프리덴 소파를 구매하기로 했다. 구매 과정에서도 사장님의 세심한 배려가 돋보였다. 이런저런 편의를 많이 봐주셔서 정말 감사했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 집에 들어온 소파는 기대 이상이었다. 사진으로 보는 것보다 실제로 앉아보면 쿠션감이 정말 좋고, 패브릭 소재도 고급스러운 느낌이 난다. 무엇보다 공간에 딱 맞는 크기와 색감이 집 전체 분위기를 살려준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프리덴 소파가 놓인 신혼집 거실&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다이닝_거실전경.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3LWkQ/dJMcagYXJvK/3iQXD5mHKjDqFtMvQLeXj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3LWkQ/dJMcagYXJvK/3iQXD5mHKjDqFtMvQLeXj1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3LWkQ/dJMcagYXJvK/3iQXD5mHKjDqFtMvQLeXj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3LWkQ%2FdJMcagYXJvK%2F3iQXD5mHKjDqFtMvQLeXj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;다이닝_거실전경.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;소파_클로즈업.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDxcCk/dJMcacI3VHI/Qj19JLmTUkCD9aQTHz6Z30/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDxcCk/dJMcacI3VHI/Qj19JLmTUkCD9aQTHz6Z30/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDxcCk/dJMcacI3VHI/Qj19JLmTUkCD9aQTHz6Z30/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDxcCk%2FdJMcacI3VHI%2FQj19JLmTUkCD9aQTHz6Z30%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;3000&quot; data-filename=&quot;소파_클로즈업.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;소파_저녁거실.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R9JYY/dJMcaioX1dY/gWoba9O765vkCVA5KbO7j0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R9JYY/dJMcaioX1dY/gWoba9O765vkCVA5KbO7j0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R9JYY/dJMcaioX1dY/gWoba9O765vkCVA5KbO7j0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR9JYY%2FdJMcaioX1dY%2FgWoba9O765vkCVA5KbO7j0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3000&quot; height=&quot;4000&quot; data-filename=&quot;소파_저녁거실.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;침대 프레임도 같이 구입했는데, 역시 만족스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동글동글한 디자인에 푹신한 감촉이 너무 마음에 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가격도 정말 괜찮게 해주셨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-14-33-58 006.jpeg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfxgc1/dJMb99Mh6jt/uPr7UyjFh0KvNsdEsS02K1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfxgc1/dJMb99Mh6jt/uPr7UyjFh0KvNsdEsS02K1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfxgc1/dJMb99Mh6jt/uPr7UyjFh0KvNsdEsS02K1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfxgc1%2FdJMb99Mh6jt%2FuPr7UyjFh0KvNsdEsS02K1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3000&quot; height=&quot;4000&quot; data-filename=&quot;KakaoTalk_Photo_2026-03-07-14-33-58 006.jpeg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사장님이 워낙 잘 대해주셔서 이 글을 꼭 써야겠다고 마음먹었다. 근데 글을 안 쓴 지 오래되어서 감이 잘 안 잡히더라. 그래서 AI인 &lt;b&gt;Cowork(Claude)&lt;/b&gt;한테 사진들과 스토리를 주고 도움을 받아 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론을 두 줄로 정리하자면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ 프리덴 소파, 생각보다 훨씬 마음에 든다. 충분히 그 가격 이상의 가치가 있다.&lt;br /&gt;✅ Cowork(Claude)는 티스토리 글도 꽤 잘 쓴다. 같이 쓸 만 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신혼집 꾸미기를 고민 중인 분들이 있다면, 광주 가구 거리 한 번 가보시길 강추드린다. 특히 프리덴 본사 쇼룸, 꼭 2층까지 올라가보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;위 글은 모두 Claude Cowork로 작성해보았습니다 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리덴 소파 사장님께 감사의 의미로 홍보 글도 작성하고 싶은데 직접 쓰기는 글을 안 쓴지가 너무 오래되서...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Cowork를 테스트해볼겸 시도해봤는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후기로는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 생각보다 잘 한다. 티스토리에 접속만 해두면 입력한 스토리에 맞게 글도 작성하고 첨부한 사진도 적절히 배치해서 넣는다. 심지어 태그까지 알아서 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 생각보다 오래 걸린다. 글 하나 작성하고 스크린샷 찍어서 확인하고, 클릭하고 스크린샷 찍고, 잘못 클릭했으면 다시 스크린샷 찍고 다른 버튼 누르고... 352단계가 걸려서 토큰을 엄청 썼다. (이럴 바에는 Github Markdown으로 작성해달라고 할 것 같긴하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Claude Cowork 이외에 Google Antigravity도 크롬 브라우저 조작 기능이 있기 때문에 충분히 가능할 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dt1Ar5/dJMcabwAfAv/lmvmXg5R3ybIPbCNnVTqB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dt1Ar5/dJMcabwAfAv/lmvmXg5R3ybIPbCNnVTqB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dt1Ar5/dJMcabwAfAv/lmvmXg5R3ybIPbCNnVTqB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdt1Ar5%2FdJMcabwAfAv%2FlmvmXg5R3ybIPbCNnVTqB0%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;3000&quot; height=&quot;1559&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1559&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;788&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X7efd/dJMcabDniSD/y5jkGKWvokYt3BHYwZ8eI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X7efd/dJMcabDniSD/y5jkGKWvokYt3BHYwZ8eI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X7efd/dJMcabDniSD/y5jkGKWvokYt3BHYwZ8eI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX7efd%2FdJMcabDniSD%2Fy5jkGKWvokYt3BHYwZ8eI1%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;788&quot; height=&quot;1148&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>나의 이야기</category>
      <category>광주가구</category>
      <category>소파추천</category>
      <category>신혼집가구</category>
      <category>프리덴소파</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/44</guid>
      <comments>https://right1203.tistory.com/44#entry44comment</comments>
      <pubDate>Sat, 7 Mar 2026 16:35:50 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] Udemy - Linux Command Line 부트캠프 강의 후기</title>
      <link>https://right1203.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;(아직 강의를 다 들은 것은 아닌 중간 후기입니다..)&lt;/s&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;글또라는 모임에서 Udemy의 강의를 수강할 수 있는 기회를 주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매일 사용하고 있지만 한 번도 제대로 공부해본 적이 없는 Linux CLI에 대해 강의를 들어보기로 했다.&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;1492&quot; data-origin-height=&quot;2874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFbl76/btsGZvZfCv7/BHrZ59WcegFQ0rhElMc020/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFbl76/btsGZvZfCv7/BHrZ59WcegFQ0rhElMc020/img.png&quot; data-alt=&quot;목차 (166개 강의...)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFbl76/btsGZvZfCv7/BHrZ59WcegFQ0rhElMc020/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFbl76%2FbtsGZvZfCv7%2FBHrZ59WcegFQ0rhElMc020%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;1492&quot; height=&quot;2874&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;2874&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;목차 (166개 강의...)&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;그리고 제목을 봤을 때는 어? 이것들 다 아는 것 같은데? 싶은 것들이 많아서 처음에는 강의를 잘못 선택했나 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 일단 들어보자 싶어서 들어봤더니 나쁘지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아는 내용은 스킵하거나 배속으로 넘겨가면서 듣기만 해도 어느정도 정리가 되는 느낌이었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의에서 명령어마다 쓸만한 옵션들을 소개해줘서 '이런 옵션도 있었구나' 하면서 재밌게 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, ls는 항상 &quot;ls -al&quot; 요것만 습관적으로 썼었는데 --sort=size, --sort=time 등으로 정렬도 생각보다 쉽게 가능한 것도 알아서 간혹 써먹고 있다.&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;그리고 grep이나 리다이렉션, 파이프 등에 대해서도 너무 가볍게만 쓰다보니 잘 몰랐는데, 강의를 들으면서 정리가 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grep으로 배운 것을 하나 말해보자면&lt;/p&gt;
&lt;pre id=&quot;code_1714309169049&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 맨날 사용하던 방식
# 이 경우, 그냥 hello 글자가 있는 라인만 나온다. 어느 라인에 hello가 있는지 알기는 어려워서 이런 경우 에디터를 열어서 확인했었다.
$ cat a.txt | grep hello


# 행의 앞 뒤 출력 + 라인 출력하기
# n이 라인을 출력하라는 것
# C1이 앞뒤 1라인 출력하라는 것
# cat a.txt | grep hello -nC1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요런 것들도 배워서 써먹고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 강의를 다 들어보지는 못 했지만, 전부 들으려고 할 필요 없이 필요한 부분들만 빠르게 듣는 정도로도 충분히 괜찮은 강의인 것 같다는 것이 개인적인 후기이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참고로 운영체제에 대한 소개부터 차근차근 들어가기 때문에 리눅스를 처음 써보는 입장에서도 괜찮은 강의겠다는 생각이 들었습니다.&lt;br /&gt;혹시 리눅스에 대해 처음 접해보시는 상황이라면 이 강의도 한 번 고려해보셔도 좋을 것 같네요.&lt;br /&gt;좋은 강의 무료로 수강할 수 있게 해주셔서 감사합니다. thanks to. 글또, 유데미&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/기타</category>
      <category>linux</category>
      <category>udemy</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/43</guid>
      <comments>https://right1203.tistory.com/43#entry43comment</comments>
      <pubDate>Sun, 28 Apr 2024 21:53:03 +0900</pubDate>
    </item>
    <item>
      <title>[검색엔진] Elasticsearch - 인덱스 교체 전략</title>
      <link>https://right1203.tistory.com/42</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Elasticsearch를 검색 엔진으로 사용하기 위해서는 다운 타임이 없어야 한다.&lt;br&gt;또한 데이터 정합성, 실시간 데이터 반영을 챙기기 위해서는 동적, 정적 색인을 진행한다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;동적 색인이란, 실시간 데이터 반영을 위해 색인을 위한 데이터의 변경사항이 발생할 때마다 감지하여 ES에 색인시키는 동작을 의미한다.&lt;br&gt;정적 색인이란, 데이터 정합성이 깨지는 이슈를 방지하기 위해 특정 주기(매일, 매주 등)마다 색인 데이터를 전체 재색인하는 동작을 의미한다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;정적 색인 시에 검색 성능 영향을 최소화하기 위해 인덱스 교체 전략이 필요해진다.&lt;br&gt;ES에서 데이터를 색인시키는 경우, 검색에 사용 중인 인덱스에 데이터를 대량으로 색인하는 경우 검색 성능이 현저히 떨어진다. e.g. 10ms -&amp;gt; 2000ms 등&lt;br&gt;이 이슈를 해결하기 위해서는 검색에 사용 중인 인덱스와 별도의 인덱스에 정적 색인을 진행한 뒤, 색인을 교체하는 것이 좋다.&lt;br&gt;이것이 대해 알아보자.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;검색 엔진이 고려해야 할 내용&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;라이브 중인 서비스가 영향을 받으면 안 된다.&lt;/li&gt;&lt;li&gt;데이터를 업데이트할 때 서비스의 TPS나 Response Time이 하락하면 안 된다.&lt;/li&gt;&lt;li&gt;모든 데이터를 빠짐없이 포함해야 한다.&lt;/li&gt;&lt;li&gt;데이터는 준실시간으로 업데이트되어야 한다.&lt;/li&gt;&lt;li&gt;동적으로 업데이트만 하는 경우, 싱크/정합성 문제가 발생할 수 밖에 없기 때문에 한 번씩 전체 데이터를 재색인해서 싱크를 맞춰줘야 한다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;ES의 문제점&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;인덱스에 몇 백만건 이상의 데이터를 연속으로 색인(정적 색인)하는 경우, 검색 성능이 아주 크게 떨어진다.&lt;/li&gt;&lt;li&gt;색인 중이 아닌 경우: 10~100ms&amp;nbsp;&amp;nbsp;/&amp;nbsp;&amp;nbsp;색인 중인 경우: 10,000ms (10초)&lt;/li&gt;&lt;li&gt;따라서 라이브 중인 인덱스가 아닌 별도의 인덱스에 색인을 하고, 색인이 완료되면 라이브 중인 인덱스를 교체하는 전략이 필요하다&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br&gt;인덱스 교체 전략&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;인덱스는 항상 두 개의 이름을 활용한다. e.g. collection-0, collection-1&lt;/li&gt;&lt;li&gt;두 개의 인덱스에 live, stage alias를 부여한다. e.g. collection-live, collection-stage&lt;/li&gt;&lt;li&gt;검색 운영용 인덱스는 live alias를 활용하고, 동적 색인은 live alias로 흘러가도록 설정한다.&lt;/li&gt;&lt;li&gt;정적 색인 시 stage alias에 색인하고, 색인 완료 시 live alias와 stage alias를 스위칭한다.&lt;/li&gt;&lt;li&gt;스위칭 시에는 데이터 정합성 이슈가 없도록 정적 색인 동안의 동적 색인을 백필하는 등의 노력을 한다.&lt;/li&gt;&lt;li&gt;정적 색인 시에는 성능 극대화를 위해 refresh interval을 꺼두고, 동적 색인 전 1초로 전환 후 스위칭한다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;위와 같은 방법으로 운영 중인 인덱스에 서비스 영향도를 낮추면서 데이터 정합성 이슈를 최소화할 수 있었다.&lt;br&gt;다만 동적 색인에 대량의 데이터가 흘러들어갈 경우 색인 속도를 조절하는 등의 추가적인 조치가 필요하다.&lt;/p&gt;</description>
      <category>스터디/검색 엔진</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/42</guid>
      <comments>https://right1203.tistory.com/42#entry42comment</comments>
      <pubDate>Sun, 14 Apr 2024 20:26:30 +0900</pubDate>
    </item>
    <item>
      <title>[검색엔진] Elasticsearch 랭킹 (2) - 유사도 점수 정규화</title>
      <link>https://right1203.tistory.com/41</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elasticsearch에서 질의어를 기준으로 유사도를 매기는 BM25 점수는 0~무한대의 값을 가진다. (물론 현실적으로는 대체로 20, 최대 100 이내의 점수일 것이다)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;검색에서 유사도 점수만 사용하는 경우도 있지만, 다른 점수(문서 자체의 품질 점수, 또는 특정 필드의 가중치 점수 등)를 같이 활용하여 랭킹에 사용하기도 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;유사도 점수를 다른 점수와 결합하여 사용하기 위해서는 그 자체로 사용할 때 몇 가지 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BM25 점수가 3, 15일 때의 동일한 품질점수 0.5는 영향력 차이가 매우 크다.&lt;/li&gt;
&lt;li&gt;BM25 점수가 어느 정도의 값을 가질지는 질의어에 따라 상당한 차이가 발생하기 때문에 품질점수의 영향력을 예측하기가 매우 어려워진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;물론, 검색된 문서들간의 상대적인 비교가 목적이기 때문에 정규화(또는 스케일링)가 반드시 필요한 것은 아니지만, 여기서는 어떤 방법이 있는지를 정리해본다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;유사도 점수를 0~1로 나오게 하기 위해서는 BM25 수식을 수정하는 방법, IDF 값을 정규화하는 방법, BM25가 아닌 다른 유사도 기법을 사용하는 방법 등이 있겠지만, 여기서는 BM25 점수 자체를 0~1 사이로 변환하는 방법에 대해서만 알아본다.&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;점수 변환 방법과 그래프 분포&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리서치를 했을 때 유사도 점수를 정규화하는 방법이 흔히 사용되지 않는 것 같기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그와 관련된 논의나 논문이 많지는 않으며, 대부분은 최근 벡터 유사도를 활용한 시멘틱 서치와의 결합 방법인 RRF, CC와 관련된 논의 위주인 것으로 보인다. (참고로 RRF에서 주로 사용하는 방법은 Min-Max Scaling인 듯하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Min-Max Scaling&lt;/h4&gt;
&lt;pre id=&quot;code_1711876240513&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;value_new = ( value - min(value) ) / ( max(value) - min(value) )&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;데이터의 분포는 변하지 않고, 0~1 사이로 값을 변환하기 위한 기법&lt;/li&gt;
&lt;li&gt;가장 베스트이지만.. min,max의 통계값을 사용해야 하기 때문에 ES 스크립트에서 구현할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: left;&quot;&gt;Saturation&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1711876297032&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;value_new = value / ( k + value )
* k가 커질수록 기울기가 완만해진다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계 없이 값을 0~1 사이로 변환할 수 있는 방법 (Sigmoid에서 각 값의 제곱이 사라진 버전)&lt;/li&gt;
&lt;li&gt;값이 증가함에 따라 점수가 증가하지만, 일정 수준 이상에서는 점수 증가가 포화(Saturate)되어 더 이상 크게 증가하지 않는 방
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BM25의 변형된 TF식의 k 값도 일종의 saturation function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단순하기에 계산 성능에서 이점이 있지만, 원래 점수의 분포가 달라진다는 단점이 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMRBfS/btsGgbstEKm/cHj1lvJTYCSJMNPbN2xBHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMRBfS/btsGgbstEKm/cHj1lvJTYCSJMNPbN2xBHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMRBfS/btsGgbstEKm/cHj1lvJTYCSJMNPbN2xBHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMRBfS%2FbtsGgbstEKm%2FcHj1lvJTYCSJMNPbN2xBHk%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;2068&quot; height=&quot;1112&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;1112&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: left;&quot;&gt;Sigmoid&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1711876349593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;value_new = value^a / ( k^a + value^a )
* k가 커질수록 기울기가 완만해진다.
* a가 커질수록 초기의 증가가 느려지고, 일정 구간 이후 증가가 빨라진다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Saturation과 유사하게 포화되는 그래프를 가지지만, a값의 조정으로 초기/후기의 증가 기울기를 조정할 수 있다.&lt;/li&gt;
&lt;li&gt;포화에 대한 개념은 동일하되 제곱의 사용으로 성능상 약점이 있기 때문에, 특정한 목적이 아니라면 Saturation 사용으로 충분할 듯하다.&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;2058&quot; data-origin-height=&quot;1120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1hXbJ/btsGdEpAOdR/X12teAEh1KbfylcNfg8Htk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1hXbJ/btsGdEpAOdR/X12teAEh1KbfylcNfg8Htk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1hXbJ/btsGdEpAOdR/X12teAEh1KbfylcNfg8Htk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1hXbJ%2FbtsGdEpAOdR%2FX12teAEh1KbfylcNfg8Htk%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;2058&quot; height=&quot;1120&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;1120&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: left;&quot;&gt;Max&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1711876385567&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;value_new = MIN(value, k) / k&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Min Max Scaling과 유사하지만, 통계 정보를 알 수 없기 때문에 점수의 상한선을 임의로 두는 방식이다.&lt;/li&gt;
&lt;li&gt;MAX값을 너무 높게 잡으면 유사도의 영향력이 줄어드는 문제가, 너무 낮게 잡으면 일정 이상의 점수는 모두 동일한 유사도를 가지는 문제가 발생한다.&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;2042&quot; data-origin-height=&quot;1128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnclmu/btsGfxJiZYP/j7RoJy8L4FVh7VOWkYc9h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnclmu/btsGfxJiZYP/j7RoJy8L4FVh7VOWkYc9h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnclmu/btsGfxJiZYP/j7RoJy8L4FVh7VOWkYc9h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcnclmu%2FbtsGfxJiZYP%2Fj7RoJy8L4FVh7VOWkYc9h1%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;2042&quot; height=&quot;1128&quot; data-origin-width=&quot;2042&quot; data-origin-height=&quot;1128&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;h4 data-ke-size=&quot;size20&quot;&gt;Max Step (가칭)&lt;/h4&gt;
&lt;pre id=&quot;code_1711876416531&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;value_new = ( MIN(value, k1) / k1 ) * w1 + (( MIN(value, k2) - MIN(value, k1) ) / (k2-k1) ) * w2&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(실제 주로 활용되는 방법은 아닌 듯하다. 그냥 내 생각대로 구현해본 방법)&lt;/li&gt;
&lt;li&gt;Max에서 k 이상의 점수가 모두 동일한 유사도를 가지는 문제를 해결하기 위해, 두 구간으로 나눠 포화 개념을 추가한 버전.&lt;/li&gt;
&lt;li&gt;0~k1까지 빠르게 증가하고, k1~k2까지는 천천히 증가하도록 설정하는 방식 (아래 그래프는 일괄적으로 k2=100, w1=0.9, w2=0.1을 적용)&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;2066&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTP6xY/btsGfw4I96O/7OB7demRtmCNQzXA9AKQO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTP6xY/btsGfw4I96O/7OB7demRtmCNQzXA9AKQO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTP6xY/btsGfw4I96O/7OB7demRtmCNQzXA9AKQO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTP6xY%2FbtsGfw4I96O%2F7OB7demRtmCNQzXA9AKQO0%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;2066&quot; height=&quot;1112&quot; data-origin-width=&quot;2066&quot; data-origin-height=&quot;1112&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 내 데이터를 활용하여 실제 BM25 분포를 살펴보기도 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 주로 사용하는 쿼리 상위 1,000개를 활용하여 살펴보았고, 쿼리별 BM25의 평균과 최대값을 각각의 기법으로 변환했을 때의 분포를 살펴보고 어떤 방법이 적절할지를 고민해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이는 회사 내의 데이터가 포함되어 있기에 상세히 기재하지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게만 적어보자면 원본 BM25 점수는 대부분 쿼리별 평균 10 이하의 점수였고 최대 30 이하의 점수 분포를 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;20 이상의 점수가 나오는 쿼리와 결과를 살펴봤을 때 그 점수가 충분히 의미가 있지는 않다고 개인적으로 판단했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로는 Max Step을 10, 20의 두 구간으로 나눠서 BM25 점수를 정규화하여 사용해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론 랭킹의 결과가 좋지 않으면 수정될 수 있다)&lt;/p&gt;</description>
      <category>스터디/검색 엔진</category>
      <category>elasticsearch</category>
      <category>유사도 정규화</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/41</guid>
      <comments>https://right1203.tistory.com/41#entry41comment</comments>
      <pubDate>Sun, 31 Mar 2024 18:21:30 +0900</pubDate>
    </item>
    <item>
      <title>[검색엔진] Elasticsearch에서의 랭킹 (1) - 스크립트</title>
      <link>https://right1203.tistory.com/40</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;p data-ke-size=&quot;size16&quot;&gt;검색 결과가 10개 수준인 경우 랭킹이 잘못되어도 품질에서 차이가 크게 안 날 수 있지만, 검색 결과가 수 천, 수 만개인 경우 품질에 상당한 차이가 발생된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES에서 랭킹을 어떻게 조정할 수 있는지, 랭킹에는 어떤 요소들이 있는지 등을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫 번째로 ES에서 스크립트를 작성하는 방법부터 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;ES에서 스크립트 작성하기&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES에서 &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html&quot;&gt;script_score&lt;/a&gt;를 이용해서 커스터마이징 랭킹을 구현할 수 있다.&lt;/li&gt;
&lt;li&gt;script_score는 script 언어를 이용해서 코드를 작성하는 방식이다.&lt;/li&gt;
&lt;li&gt;script 언어는 &lt;code&gt;painless&lt;/code&gt;, &lt;code&gt;expression&lt;/code&gt;, &lt;code&gt;mustache&lt;/code&gt;, &lt;code&gt;java&lt;/code&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://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting.html#scripting-available-languages&quot;&gt;Scripting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;이전에는 &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;javascript&lt;/code&gt;를 지원하였으나.. 5.x버전부터 지원하지 않는다.&lt;/li&gt;
&lt;li&gt;java는 &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting-engine.html#modules-scripting-engine&quot;&gt;특수한 목적&lt;/a&gt;에서만 사용하기에 랭킹을 위한 스크립트로는 사용할 일이 없어보인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;script language&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  painless가 훨씬 편하다. painless로 구현하고, 성능 이슈가 있을 때 expression을 써도 늦지 않을 듯하다. 언어와 Search Templates은 같이 사용할 수 있다.&lt;/blockquote&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://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting-painless.html&quot;&gt;painless&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엘라스틱서치 자체에서 개발한 스크립트 언어.&lt;/li&gt;
&lt;li&gt;기본으로 적용되며, Java Style의 문법으로 작성한다.&lt;/li&gt;
&lt;li&gt;안전하고, 쉽고, 빠르다.&lt;/li&gt;
&lt;li&gt;쿼리 실행, 데이터 전송, 텍스트 분석, 정렬 알고리즘 등의 다양한 목적으로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting-expression.html&quot;&gt;expression&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자 연산에 특화되어 있는 Lucene 기반 스크립트 언어.&lt;/li&gt;
&lt;li&gt;숫자 연산에서는 매우 빠른 성능을 보장한다.&lt;/li&gt;
&lt;li&gt;숫자 연산에만 특화되어 있기 때문에 I/O나 동적 메서드 호출, 루프 연산, 조건 연산 등은 불가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커스텀 랭킹이나 정렬 기능을 구현하는 것에 특화되어 사용한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다만 거리 연산 등을 직접 지원하지 않기 때문에..&amp;nbsp; 거리 개념이 들어가거나 함수가 포함되면 구현이 불가능해질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-template.html&quot;&gt;mustache&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트 언어라기보다는 Search Templates이다.&lt;/li&gt;
&lt;li&gt;미리 구성한 Query DSL에 특정 부분을 &lt;code&gt;{{variable}}&lt;/code&gt; 과 같이 변수화해서 템플릿을 만들어두고, 변수만 대입하여 쿼리를 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;painless&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting-using.html&quot;&gt;기본 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;정말 많은 용도에 가져다 쓸 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 실행, 정렬 알고리즘, 데이터 업데이트, 데이터 일부 삭제 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/scripts-and-search-speed.html&quot;&gt;script cache&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트를 캐싱해두어 매번 스크립트를 가져오는 비효율을 제거하는 방법&lt;/li&gt;
&lt;li&gt;캐시는 디폴트로 적용되어 업데이트가 발생했을 때만 컴파일된다. expire, max_size에 따라 캐시가 퇴출될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스크립트 언어를 사용하면, 매번 결과를 새로 연산해야하기 때문에 기본 동작에 비해 느릴 수 밖에 없다.&lt;/li&gt;
&lt;li&gt;만약 매번 검색 시마다 달라지는 결과(유사도, 특정 좌표와의 거리 등)가 아니라면 ingest pipeline으로 결과를 미리 생성하여 문서에 포함시키는 것으로 속도를 향상시킬 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/ingest.html&quot;&gt;ingest pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/script-processor.html&quot;&gt;script processor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/dissect.html&quot;&gt;Dissecting&lt;/a&gt;(문자열 패턴 파싱), &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/grok.html&quot;&gt;Grok&lt;/a&gt;(로그 파싱)도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌트인 변수&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;변수명&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ctx&lt;/td&gt;
&lt;td&gt;context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;현재 문서(doc)의 정보를 담고 있음&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ctx._index&lt;/td&gt;
&lt;td&gt;인덱스명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ctx._id&lt;/td&gt;
&lt;td&gt;문서ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ctx._source&lt;/td&gt;
&lt;td&gt;문서의 소스 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;params&lt;/td&gt;
&lt;td&gt;params에서 전달한 값을 get() 또는 [] 리스트 접근자로 사용할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;doc&lt;/td&gt;
&lt;td&gt;doc의 각 필드에 접근할 수 있음 (e.g. &lt;span style=&quot;background-color: #f9f9f9; color: #333333; text-align: start;&quot;&gt;doc['fieldName'])&lt;/span&gt;&lt;br /&gt;열 기반으로 필드가 저장되어 있어서 접근하려는 필드 이외를 처리하지 않기에 성능이 뛰어나다. &lt;br /&gt;값이 없는 경우 에러가 발생하기에 doc.containsKey('field')로 체크하는 것이 좋다. &lt;br /&gt;분석된 텍스트 필드에는 접근이 안 된다. 접근을 가능하게 하려면 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/text.html#fielddata-mapping-param&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;fielddata&lt;/span&gt;&lt;/a&gt;를 활성화해야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;_score&lt;/td&gt;
&lt;td&gt;해당 문서의 유사도 스코어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;_source&lt;/td&gt;
&lt;td&gt;_doc과 같이 소스 문서의 필드에 직접 접근을 할 수 있으나, 문서 전체를 파싱을 한 후 접근하는 방식이어서 일반적으로 _doc보다 성능이 떨어진다. 결과당 여러 필드를 반환하는데에 최적화되어 있다. 검색 결과로 히트된 N개의 문서에서 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-fields.html#script-fields&quot; data-token-index=&quot;1&quot;&gt;&lt;span&gt;script fields&lt;/span&gt;&lt;/a&gt;를 생성할 때 사용하는 것이 좋다.&lt;br /&gt;map-of-maps 구조여서 _source.name.first와 같이 접근할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-lang-spec.html&quot;&gt;전체 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-comments.html&quot;&gt;주석&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code class=&quot;language-bash&quot;&gt;  // single-line comment
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-bash&quot;&gt;  /* multi-
       line
       comment */&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-keywords.html&quot;&gt;Keywords&lt;/a&gt; (Built-in Functions)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if, else, while, for, in, continue 등의 Java에서 사용할 수 있는 키워드들&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-functions.html&quot;&gt;Functions&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트의 시작부에 선언할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-bash&quot;&gt;boolean isNegative(def x) { x &amp;lt; 0 }
...
if (isNegative(someVar)) {
...
}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-lambdas.html&quot;&gt;Lambda&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java의 Lambda와 동일하게 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/painless/7.17/painless-regexes.html&quot;&gt;정규 표현식&lt;/a&gt; (Regexes)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Pattern p = /[aeiou]/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;Query DSL - Ranking&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-rank-feature-query.html&quot;&gt;Rank feature query&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;numeric 타입의 &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/rank-feature.html&quot;&gt;rank_feature&lt;/a&gt;, &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/rank-features.html&quot;&gt;rank_features&lt;/a&gt; 필드를 기반으로 유사도 점수(relevance score)를 조정하는 방법&lt;/li&gt;
&lt;li&gt;즉 pagerank, url_length, topics 등의 이미 랭크를 매기기 위한 점수들을 필드로 만들어뒀을 때, 유사도 점수에 가중치를 부여하여 조정하는 방법이다.&lt;/li&gt;
&lt;li&gt;최근성이나 거리 계산과 같이 쿼리를 할 때마다 달라지는 경우에는 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-query.html&quot;&gt;Script Query&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리를 할 때 script를 사용해서 필터링, 가공을 할 수 있는 방법&lt;/li&gt;
&lt;li&gt;Runtime fields와 유사하다&lt;/li&gt;
&lt;li&gt;params로 파라미터를 줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /_search
{
  &quot;query&quot;: {
    &quot;bool&quot;: {
      &quot;filter&quot;: {
        &quot;script&quot;: {
          &quot;script&quot;: {
            &quot;source&quot;: &quot;doc['num1'].value &amp;gt; params.param1&quot;,
            &quot;lang&quot;: &quot;painless&quot;,
            &quot;params&quot;: {
              &quot;param1&quot;: 5
            }
          }
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Script score&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default로 사용하는 유사도 스코어 이외에, 본인이 스코어를 정의하고 싶은 경우 사용한다.&lt;/li&gt;
&lt;li&gt;쿼리 내에서 script_score를 사용하는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유사도&lt;/b&gt; &lt;b&gt;스코어 펑션 자체가 비용이 커서 이미 필터링된 데이터에 대해 계산이 필요할 때 유용하다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사실상 유사도 스코어를 대체하기 위한 방법이라고 봐야할 듯&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Script_score가 계산한 최종 점수는 반드시 0 또는 양수여야 한다. (음수 불가능)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 사용방법&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /_search
{
  &quot;query&quot;: {
    &quot;script_score&quot;: {
      &quot;query&quot;: {
        &quot;match&quot;: { &quot;message&quot;: &quot;elasticsearch&quot; }
      },
      &quot;script&quot;: {
        &quot;source&quot;: &quot;doc['my-int'].value / 10 &quot;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파라미터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;query: 쿼리 구문&lt;/li&gt;
&lt;li&gt;script: score를 계산할 스크립트&lt;/li&gt;
&lt;li&gt;min_score(Optional): 검색 결과에 포함할 최소 스코어&lt;/li&gt;
&lt;li&gt;boost: 가중치 (기본은 1.0)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#script-score-predefined-functions&quot;&gt;predefined functions&lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;painless 스크립트에 미리 정의되어 있는 functions.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 구현하는 것보다 predefined를 사용하는 것이 좋다고 한다. (내부 메커니즘에 의해 더 효율적이라고 함)&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://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#script-score-saturation&quot;&gt;Saturation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#script-score-sigmoid&quot;&gt;Sigmoid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#random-score-function&quot;&gt;Random score function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#decay-functions-numeric-fields&quot;&gt;Decay functions for numeric fields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#decay-functions-geo-fields&quot;&gt;Decay functions for geo fields&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거리 기반 점수에서, 특정 범위가 넘어갈 때마다 더 낮은 점수(가중치)를 주려면 이걸 사용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#decay-functions-date-fields&quot;&gt;Decay functions for date fields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-script-score-query.html#script-score-functions-vector-fields&quot;&gt;Functions for vector fields&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-pinned-query.html&quot;&gt;Pinned Query&lt;/a&gt;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html&quot;&gt;Function score&lt;/a&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;function_score를 사용하기 위해서는 쿼리와 function을 미리 정의해두어야 한다.&lt;/li&gt;
&lt;li&gt;여러 가중치를 두고 사용하기에 가장 적절해보인다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 스코어들의 값 범위가 다르다.&lt;/li&gt;
&lt;li&gt;이 범위를 조정하기 위해 weight를 function마다 조정하여야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 사용방법&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 가장 기본 사용방법
GET /_search
{
  &quot;query&quot;: {
    &quot;function_score&quot;: {
      &quot;query&quot;: { &quot;match_all&quot;: {} },
      &quot;boost&quot;: &quot;5&quot;,
      &quot;random_score&quot;: {}, 
      &quot;boost_mode&quot;: &quot;multiply&quot;
    }
  }
}

# functinos combile
GET /_search
{
  &quot;query&quot;: {
    &quot;function_score&quot;: {
      &quot;query&quot;: { &quot;match_all&quot;: {} },
      &quot;boost&quot;: &quot;5&quot;, 
      &quot;functions&quot;: [
        {
          &quot;filter&quot;: { &quot;match&quot;: { &quot;test&quot;: &quot;bar&quot; } },
          &quot;random_score&quot;: {}, 
          &quot;weight&quot;: 23
        },
        {
          &quot;filter&quot;: { &quot;match&quot;: { &quot;test&quot;: &quot;cat&quot; } },
          &quot;weight&quot;: 42
        }
      ],
      &quot;max_boost&quot;: 42,
      &quot;score_mode&quot;: &quot;max&quot;,
      &quot;boost_mode&quot;: &quot;multiply&quot;,
      &quot;min_score&quot;: 42
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스코어 계산 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;score_mode: 각 스코어들이 어떻게 결합(combine)될 것인지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;multiply: 스코어들 간의 곱셈 (default)&lt;/li&gt;
&lt;li&gt;sum&lt;/li&gt;
&lt;li&gt;avg&lt;/li&gt;
&lt;li&gt;first&lt;/li&gt;
&lt;li&gt;max&lt;/li&gt;
&lt;li&gt;min&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;boost_mode: score_mode를 통해 계산된 하나의 스코어가 쿼리의 스코어(유사도 스코어)와 어떻게 결합될 것인지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;multiply&lt;/li&gt;
&lt;li&gt;replace: 쿼리 스코어를 사용하지 않고 대체한다.&lt;/li&gt;
&lt;li&gt;sum&lt;/li&gt;
&lt;li&gt;avg&lt;/li&gt;
&lt;li&gt;max&lt;/li&gt;
&lt;li&gt;min&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지원하는 스코어 계산 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;script_score
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트 표현식으로 score를 계산하는 방법&lt;/li&gt;
&lt;li&gt;반드시 0 또는 양수로 표현해야 한다.&lt;/li&gt;
&lt;li&gt;스크립트는 빠른 실행을 위해 컴파일 후에 캐싱된다. (재사용을 위해서는 변수는 params를 사용한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Random
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랜덤으로 값을 생성하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Field Value factor
&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;script를 사용할 때의 오버헤드를 피할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-function-score-query.html#function-decay&quot;&gt;Decay functions&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자, 좌표, 날짜 값에 대해 특정 거리를 넘어설 때마다 감쇠하여 점수를 계산하는 방식&lt;/li&gt;
&lt;li&gt;여러 필드를 같이 사용할 수도 있다.&lt;/li&gt;
&lt;li&gt;지원하는 decay functions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gauss&lt;/li&gt;
&lt;li&gt;exp&lt;/li&gt;
&lt;li&gt;linear&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1708255792154&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 사용 방법
&quot;DECAY_FUNCTION&quot;: { 
    &quot;FIELD_NAME&quot;: { 
          &quot;origin&quot;: &quot;11, 12&quot;, # 기준 위치
          &quot;scale&quot;: &quot;2km&quot;, # 감쇠함수가 적용되는 단위
          &quot;offset&quot;: &quot;0km&quot;, # offset 이후부터만 decay 적용
          &quot;decay&quot;: 0.33 # scale마다 decay*score 값이 적용될 수 있도록 만든다.
    }
}

# gauss를 사용한 예시
GET /_search
{
  &quot;query&quot;: {
    &quot;function_score&quot;: {
      &quot;gauss&quot;: {
        &quot;@timestamp&quot;: {
          &quot;origin&quot;: &quot;2013-09-17&quot;, 
          &quot;scale&quot;: &quot;10d&quot;,
          &quot;offset&quot;: &quot;5d&quot;,         
          &quot;decay&quot;: 0.5            
        }
      }
    }
  }
}&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;h1&gt;랭킹 스코어 구현하기&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리서치한 결과, 여러 스코어를 가중치를 두고 곱할 수도 있으며 predefined function과 decay function을 사용할 수 있는 Function Score 방식이 적절해보인다.&lt;/li&gt;
&lt;li&gt;필요한 스코어들을 구현해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;유사도(similarity)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default로 BM25의 유사도를 사용한다.&lt;/li&gt;
&lt;li&gt;_score로 나오는 값인데, 값이 0~1 사이가 아니기 때문에 어떻게 조정할 것인지가 중요하다.&lt;/li&gt;
&lt;li&gt;sigmoid(로지스틱 함수)를 적용하는 건 어떨까?&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;&quot;functions&quot;: [
  {
    &quot;script_score&quot;: {
      &quot;script&quot;: {
        &quot;source&quot;: &quot;1 / (1 + Math.exp(-1 * _score))&quot;
      }
    }
  }
]&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다만 logistic function은 0일 때 0.5, 음수일 때 0~0.5가 나온다.&lt;/li&gt;
&lt;li&gt;_score는 유사도 기반의 BM25 스코어이기 때문에 반드시 양수만 나온다.&lt;/li&gt;
&lt;li&gt;즉, 위의 로직은 반드시 0.5 ~ 1사이만 나온다.&lt;/li&gt;
&lt;li&gt;이것을 해결하기 위해 score*2-1을 해서 0~1사이로 바꿔보는 방법도 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다만 점수의 분포가 달라진다. max 값을 설정하는 방법도 있을 듯함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;&quot;functions&quot;: [
  {
    &quot;script_score&quot;: {
      &quot;script&quot;: {
        &quot;source&quot;: &quot;2 / (1 + Math.exp(-1 * _score)) - 1&quot;
      }
    }
  }
]
# _score를 상수로 바꿔서 돌려보면 0~1 사이로 잘 나온다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;거리 계산(distance)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;근접도를 계산하는 방법은 몇 가지 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;planeDistance
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Euclidean distance, 두 좌표의 직선거리를 계산하는 방식&lt;/li&gt;
&lt;li&gt;거리가 멀어질수록 부정확하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;arcDistance
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;great-circle distance, 지구를 구체로 두고 계산하는 방식&lt;/li&gt;
&lt;li&gt;더 정확한 거리를 계산할 수 있지만 속도가 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;decay function
&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://github.com/elastic/elasticsearch/blob/main/server/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionBuilder.java&quot;&gt;https://github.com/elastic/elasticsearch/blob/main/server/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionBuilder.java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;arcDistance를 쓰는 것으로 보인다.&lt;/li&gt;
&lt;li&gt;offset과 scale을 두고 거리마다 가중치를 부여할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;decay function을 이용해보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 function을 쓰는 것이 좋을까?&lt;/li&gt;
&lt;li&gt;가까운 거리라면 가중치를 크게 두지 않고, 먼 거리일수록 가중치가 떨어지는 것이 좋아보인다.&lt;/li&gt;
&lt;li&gt;gauss를 사용해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;&quot;functions&quot;: [
  {
    &quot;gauss&quot;: {
      &quot;location&quot;: {
        &quot;origin&quot;: {
          &quot;lat&quot;: 37.3,
          &quot;lon&quot;: 126.8
        },
        &quot;scale&quot;: &quot;5km&quot;,
        &quot;offset&quot;: &quot;0km&quot;,
        &quot;decay&quot;: 0.5
      }
    }
  }
]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최근성(recency)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거리 계산과 동일하게 decay를 사용해도 될 듯.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;functions&quot;: [
  {
    &quot;linear&quot;: {
      &quot;@timestamp&quot;: {
        &quot;origin&quot;: &quot;now&quot;,
        &quot;scale&quot;: &quot;120d&quot;,
        &quot;offset&quot;: &quot;30d&quot;,
        &quot;decay&quot;: 0.5
      }
    }
  }
]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;퀄리티(Quality)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리와 별개로 문서 자체에 랭킹을 부여하기 위한 요소.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 필드명으로 점수를 부여하고, 랭킹 조정을 위한 쿼리에서 해당 필드의 점수를 사용하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서 좀 더 자세히 알아보자&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;가중치 부여하기&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치를 어떻게 부여하나?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;weight에 가중치를 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가중치를 어떻게 관리하나?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치가 바뀔 수 있기에, ES 스크립트에서 가중치를 고정하는 것보다는 ES를 사용하는 API 단에서 가중치를 조정하자.&lt;/li&gt;
&lt;li&gt;예시로 복합 랭킹 점수를 쿼리로 구현해보자&lt;/li&gt;
&lt;li&gt;랭킹은 아래의 요소들을 가정하고 만들었다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거리 점수: 문서(장소 데이터)의 위치와 사용자의 위치가 멀수록 점수가 낮아진다.&lt;/li&gt;
&lt;li&gt;유사도 점수: 쿼리의 BM25 점수에 정규화를 씌운 점수 (정규화를 이렇게 하는게 적절한지는 좀 더 알아봐야할 듯&lt;/li&gt;
&lt;li&gt;최신성 점수: 문서의 수정일자가 오래될수록 점수가 낮아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;&quot;functions&quot;: [
  {
    &quot;script_score&quot;: {
      &quot;script&quot;: {
        &quot;source&quot;: &quot;2 / (1 + Math.exp(-1 * _score)) - 1&quot;
      }
    },
    &quot;weight&quot;: 0.33
  },
  {
    &quot;gauss&quot;: {
      &quot;location&quot;: {
        &quot;origin&quot;: {
          &quot;lat&quot;: 37.3,
          &quot;lon&quot;: 126.8
        },
        &quot;scale&quot;: &quot;5km&quot;,
        &quot;offset&quot;: &quot;0km&quot;,
        &quot;decay&quot;: 0.5
      }
    },
    &quot;weight&quot;: 0.33
  },
  {
    &quot;linear&quot;: {
      &quot;@timestamp&quot;: {
        &quot;origin&quot;: &quot;now&quot;,
        &quot;scale&quot;: &quot;120d&quot;,
        &quot;offset&quot;: &quot;30d&quot;,
        &quot;decay&quot;: 0.5
      }
    },
    &quot;weight&quot;: 0.33
  }
],
&quot;score_mode&quot;: &quot;sum&quot;,
&quot;boost_mode&quot;: &quot;replace&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랭킹을 구현하기 위해 ES에서 스크립트를 어떻게 구현하면 되는지를 먼저 알아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시에서는 거리, 유사도, 최신성을 섞어서 랭킹 점수를 만들어봤지만... 실제로 랭킹을 구성하기 위해 어떤 요소들이 있고 어떻게 사용할 수 있을지를 다음 글에서 좀 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀄리티 점수, 근접성(Proximity) 점수 등도 같이 알아보고, 예시로 ES 검색 랭킹을 구현해보는 것을 목표로 한다.&lt;/p&gt;</description>
      <category>elasticsearch</category>
      <category>검색엔진</category>
      <category>랭킹</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/40</guid>
      <comments>https://right1203.tistory.com/40#entry40comment</comments>
      <pubDate>Sun, 18 Feb 2024 20:41:22 +0900</pubDate>
    </item>
    <item>
      <title>[검색엔진] 검색 엔진 리서치</title>
      <link>https://right1203.tistory.com/39</link>
      <description>&lt;h1&gt;검색 엔진&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검색 엔진이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 엔진(Search Engine)은 사용자가 웹이나 문서에서 특정 키워드를 검색할 때 관련된 정보를 찾기 위한 소프트웨어 시스템&lt;/b&gt;을 의미한다. 대개는 비정형화된 텍스트에 대한 검색을 의미하며, 그림이나 비디오 등의 멀티미디어도 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서의 검색 엔진은 &lt;b&gt;정보 검색(IR, Information Retrieval)의 일종&lt;/b&gt;으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IR은 문서나 데이터 소스에서 관련 정보를 검색하고 제공하는 과정을 의미하며, 대개는 텍스트 기반의 정보 검색에 적용되어 사용자의 요구사항과 일치하는 문서를 찾는 것을 목적으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IR은 사용자의 검색 쿼리를 분석하고 인덱스된 문서를 검색하여 정보를 찾는 등의 문서 검색, 질의 처리, 검색 결과 평가 등의 다양한 기술을 포함하는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 엔진은 텍스트 획득, 텍스트 변환(파싱, 불용어 제거, 오타 교정 등), 색인 생성, 랭크 부여, 실시간 검색 결과 제공 등의 기능을 제공&lt;/b&gt;한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검색 엔진의 종류 (OpenSource)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;meilisearch는 Elasticsearch에 비해 구성하고 사용하기 매우 쉬우면서도 다양한 기능을 제공하기에 고려해볼만하다.&lt;br /&gt;다만 Elasticsearch는 레퍼런스가 많고  깊게 커스터마이징할 수 있는 기능이 많다.&lt;br /&gt;개인적으로는 내가 만든 서비스에 좋은 검색 엔진을 쉽게 붙이고 싶은 니즈라면 meilisearch, 검색 엔진이 상당히 중요한 서비스라면 Elasticsearch를 사용할 것 같다.&lt;/blockquote&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;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/59LQo/btsDLr5FjPe/7YtO0u1irPpuqFJj0K4s51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/59LQo/btsDLr5FjPe/7YtO0u1irPpuqFJj0K4s51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/59LQo/btsDLr5FjPe/7YtO0u1irPpuqFJj0K4s51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F59LQo%2FbtsDLr5FjPe%2F7YtO0u1irPpuqFJj0K4s51%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;1082&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://github.com/elastic/elasticsearch&quot;&gt;ElasticSearch&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1705814765206&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - elastic/elasticsearch: Free and Open, Distributed, RESTful Search Engine&quot; data-og-description=&quot;Free and Open, Distributed, RESTful Search Engine. Contribute to elastic/elasticsearch development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/elastic/elasticsearch&quot; data-og-url=&quot;https://github.com/elastic/elasticsearch&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p1EBU/hyU5Khypss/SkKKBA5Doo6Vn0GneEspxk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/elastic/elasticsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/elastic/elasticsearch&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p1EBU/hyU5Khypss/SkKKBA5Doo6Vn0GneEspxk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - elastic/elasticsearch: Free and Open, Distributed, RESTful Search Engine&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Free and Open, Distributed, RESTful Search Engine. Contribute to elastic/elasticsearch development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ElasticSearch 섹션을 참고하자.&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;&lt;a href=&quot;https://github.com/meilisearch/meilisearch&quot;&gt;meilisearch&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1705814763451&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - meilisearch/meilisearch: A lightning-fast search API that fits effortlessly into your apps, websites, and workflow&quot; data-og-description=&quot;A lightning-fast search API that fits effortlessly into your apps, websites, and workflow - GitHub - meilisearch/meilisearch: A lightning-fast search API that fits effortlessly into your apps, webs...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/meilisearch/meilisearch&quot; data-og-url=&quot;https://github.com/meilisearch/meilisearch&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byhOIf/hyU84Ma09J/HHW0SUZIWnVLHcrQahXtD0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://github.com/meilisearch/meilisearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/meilisearch/meilisearch&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byhOIf/hyU84Ma09J/HHW0SUZIWnVLHcrQahXtD0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&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;GitHub - meilisearch/meilisearch: A lightning-fast search API that fits effortlessly into your apps, websites, and workflow&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A lightning-fast search API that fits effortlessly into your apps, websites, and workflow - GitHub - meilisearch/meilisearch: A lightning-fast search API that fits effortlessly into your apps, webs...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0k28K/btsDI8zxi4U/UmyUD4tMNShC6CUKdKqJCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0k28K/btsDI8zxi4U/UmyUD4tMNShC6CUKdKqJCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0k28K/btsDI8zxi4U/UmyUD4tMNShC6CUKdKqJCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0k28K%2FbtsDI8zxi4U%2FUmyUD4tMNShC6CUKdKqJCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1504&quot; height=&quot;934&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;meilisearch는 검색 관련 기능(실시간 검색, 오타 교정, 동의어 등)이 기본적으로 탑재되어 있고, 빠른 검색 속도로 인해 간단한 설정만으로도 높은 퀄리티의 검색 엔진을 구성할 수 있어서 인기를 얻고 있는 검색 엔진이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ES와 비교해서도 상당히 많은 기능과 커스터마이징, 높은 퍼포먼스를 보여주기 때문에 충분히 고려해볼만하다. 다만 ES와 달리 레퍼런스를 찾기 쉽지 않다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예제 : &lt;a href=&quot;https://where2watch.meilisearch.com/&quot;&gt;https://where2watch.meilisearch.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징 (Officially)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Search-as-you-type(실시간 검색결과 반환):&lt;/b&gt; find search results in less than 50 milliseconds&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/getting_started/customizing_relevancy.html#typo-tolerance&quot;&gt;Typo tolerance&lt;/a&gt;(오타 허용):&lt;/b&gt; get relevant matches even when queries contain typos and misspellings&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/advanced/filtering_and_faceted_search.html&quot;&gt;Filtering and faceted search&lt;/a&gt;(필터링):&lt;/b&gt; enhance your user's search experience with custom filters and build a faceted search interface in a few lines of code&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/advanced/sorting.html&quot;&gt;Sorting&lt;/a&gt;(랭킹 커스터마이즈):&lt;/b&gt; sort results based on price, date, or pretty much anything else your users need&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/getting_started/customizing_relevancy.html#synonyms&quot;&gt;Synonym support&lt;/a&gt;(동의어, 유의어 지원):&lt;/b&gt; configure synonyms to include more relevant content in your search results&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/advanced/geosearch.html&quot;&gt;Geosearch&lt;/a&gt;(위치를 고려한 검색):&lt;/b&gt; filter and sort documents based on geographic data&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/what_is_meilisearch/language.html&quot;&gt;Extensive language support&lt;/a&gt;(한글에 최적화):&lt;/b&gt; search datasets in any language, with optimized support for Chinese, Japanese, Hebrew, and languages using the Latin alphabet&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/security/master_api_keys.html&quot;&gt;Security management&lt;/a&gt;(보안 관리):&lt;/b&gt; control which users can access what data with API keys that allow fine-grained permissions handling&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/security/tenant_tokens.html&quot;&gt;Multi-Tenancy&lt;/a&gt;(데이터에 대한 개별 액세스 권한):&lt;/b&gt; personalize search results for any number of application tenants&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Highly Customizable(커스터마이징):&lt;/b&gt; customize Meilisearch to your specific needs or use our out-of-the-box and hassle-free presets&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://docs.meilisearch.com/reference/api/overview.html&quot;&gt;RESTful API&lt;/a&gt;:&lt;/b&gt; integrate Meilisearch in your technical stack with our plugins and SDKs&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Easy to install, deploy, and maintain&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내가 느낀 좋은 점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;언어별 SDK 제공&lt;/b&gt;: JS, Python, Java, Go, Rust 등의 많은 언어의 SDK를 공식 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프론트 검색 박스(&lt;a href=&quot;https://github.com/meilisearch/instant-meilisearch&quot;&gt;Instant-meilisearch&lt;/a&gt;)&lt;/b&gt;: 검색 박스를 따로 구현하지 않아도 라이브러리로 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/getting_started/quick_start.html&quot;&gt;&lt;b&gt;Document가 잘 작성되어 있음&lt;/b&gt;&lt;/a&gt;: Elasticsearch는 문서는 방대하고 잘 작성되어 있지만 쉽지만은 않은데 비해, meilisearch의 문서는 상당히 쉬운 구조로 잘 작성되어 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쿼리가 Elasticsearch에 비해 매우 간단함&lt;/b&gt;: Elasticsearch의 DSL을 어렵게 작성하지 않아도 되고 SQL 문법과 상당히 유사하게 사용할 수 있다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커스터미이징 이 매우 쉬움&lt;/b&gt;: 플러그인이 따로 있지 않은 대신 동의어나 불용어, 오타 수정 등의 기본 기능이 매우 강력하고 사용하기 쉽다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.meilisearch.com/learn/what_is_meilisearch/comparison_to_alternatives.html#features&quot;&gt;ElasticSearch, Algolia, Typesense와의 비교&lt;/a&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;2000&quot; data-origin-height=&quot;3055&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFQNVK/btsDKzbNm67/EPD1eL2kEct1GvHkSTsK5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFQNVK/btsDKzbNm67/EPD1eL2kEct1GvHkSTsK5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFQNVK/btsDKzbNm67/EPD1eL2kEct1GvHkSTsK5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFQNVK%2FbtsDKzbNm67%2FEPD1eL2kEct1GvHkSTsK5k%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;3055&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;3055&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1705814661102&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 필터링해서 Batman 검색
client.index('movies').search('Batman', {
  'filter': 'release_date &amp;gt; 795484800 AND (director = &quot;Tim Burton&quot; OR director = &quot;Christopher Nolan&quot;)'
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://github.com/zinclabs/zincsearch&quot;&gt;zincsearch&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1705814755340&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - zincsearch/zincsearch: ZincSearch . A lightweight alternative to elasticsearch that requires minimal resources, written&quot; data-og-description=&quot;ZincSearch . A lightweight alternative to elasticsearch that requires minimal resources, written in Go. - GitHub - zincsearch/zincsearch: ZincSearch . A lightweight alternative to elasticsearch tha...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/zinclabs/zincsearch&quot; data-og-url=&quot;https://github.com/zincsearch/zincsearch&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dKaP2F/hyU8QUHACK/GaRIcCZFkrBgAGfE9iDyJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/zinclabs/zincsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/zinclabs/zincsearch&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dKaP2F/hyU8QUHACK/GaRIcCZFkrBgAGfE9iDyJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - zincsearch/zincsearch: ZincSearch . A lightweight alternative to elasticsearch that requires minimal resources, written&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ZincSearch . A lightweight alternative to elasticsearch that requires minimal resources, written in Go. - GitHub - zincsearch/zincsearch: ZincSearch . A lightweight alternative to elasticsearch tha...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES를 구성하기에는 너무 간단한 앱일 때, 가벼운 검색 엔진을 구현해주는 오픈소스.&lt;/li&gt;
&lt;li&gt;예제 : &lt;a href=&quot;https://playground.dev.zincsearch.com/&quot;&gt;https://playground.dev.zincsearch.com&lt;/a&gt; (admin / Complexpass#123)&lt;/li&gt;
&lt;li&gt;특징 (Offically)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Provides full text indexing capability&lt;/li&gt;
&lt;li&gt;Single binary for installation and running. Binaries available under releases for multiple platforms.&lt;/li&gt;
&lt;li&gt;Web UI for querying data written in Vue&lt;/li&gt;
&lt;li&gt;Compatibility with Elasticsearch APIs for ingestion of data (single record and bulk API)&lt;/li&gt;
&lt;li&gt;Out of the box authentication&lt;/li&gt;
&lt;li&gt;Schema less - No need to define schema upfront and different documents in the same index can have different fields.&lt;/li&gt;
&lt;li&gt;Index storage in disk (default), s3 or minio (deprecated)&lt;/li&gt;
&lt;li&gt;aggregation support&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://github.com/apache/lucene&quot;&gt;Apache Lucene(루씬)&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1705814785614&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - apache/lucene: Apache Lucene open-source search software&quot; data-og-description=&quot;Apache Lucene open-source search software. Contribute to apache/lucene development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/apache/lucene&quot; data-og-url=&quot;https://github.com/apache/lucene&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eoNYC1/hyU5PJT2Hw/fxlxbd2KIxoLaprAbKys71/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/apache/lucene&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/apache/lucene&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eoNYC1/hyU5PJT2Hw/fxlxbd2KIxoLaprAbKys71/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - apache/lucene: Apache Lucene open-source search software&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Apache Lucene open-source search software. Contribute to apache/lucene development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java로 구성된 오픈소스 형태의 검색 엔진 &lt;b&gt;라이브러리&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;melilsearch, zincsearch 등과 다르게 검색 플랫폼을 제공하는 것이 아닌, 검색 어플리케이션/플랫폼을 개발하는데에 사용하는 라이브러리이기 때문에 검색 시스템으로 고려할 필요는 없지만 개념은 이해하고 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Elasticsearch, Apache Solr 등에서 Lucene을 사용하여 검색 엔진 플랫폼을 구성하고 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Full-text Search: Lucene은 Full Text 검색에 특화되어 있어서 대용량 텍스트 데이터를 빠르게 검색할 수 있다.&lt;/li&gt;
&lt;li&gt;Indexing: 텍스트 검색을 효율적으로 하기 위해 역색인 기능을 제공하고, 역색인을 통해 키워드 중심으로 검색할 수 있게 한다.&lt;/li&gt;
&lt;li&gt;Performance: 역색인, 캐싱, 멀티 쓰레드, 인덱스 세그먼트 등을 이용하여 높은 성능을 보인다.&lt;/li&gt;
&lt;li&gt;Multi-language Supprt: Java 이외에 Pascal, Ruby, Python, C++ 등의 언어를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;Support for complex queries: boolean, wildcard, phrase 쿼리 등 복잡한 쿼리를 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;Faceted search(패싯 검색): faceted navigation, guided navigation이라고도 불리며, 수많은 정보들 사이에서 속성이나 카테고리를 통해 범위를 좁혀가며 필요한 정보를 골라내는 검색 방법을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://github.com/apache/solr&quot;&gt;Apache Solr(솔라)&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1705814808326&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - apache/solr: Apache Solr open-source search software&quot; data-og-description=&quot;Apache Solr open-source search software. Contribute to apache/solr development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/apache/solr&quot; data-og-url=&quot;https://github.com/apache/solr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YZmjB/hyU5Ml551v/MCyuAGglp7T8KxLtjCgfY1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/apache/solr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/apache/solr&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YZmjB/hyU5Ml551v/MCyuAGglp7T8KxLtjCgfY1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - apache/solr: Apache Solr open-source search software&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Apache Solr open-source search software. Contribute to apache/solr development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&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;1305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqZPxp/btsDGkHtWqJ/pBzqaogRe0xtYv4cgpAAAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqZPxp/btsDGkHtWqJ/pBzqaogRe0xtYv4cgpAAAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqZPxp/btsDGkHtWqJ/pBzqaogRe0xtYv4cgpAAAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeqZPxp%2FbtsDGkHtWqJ%2FpBzqaogRe0xtYv4cgpAAAk%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;1305&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1305&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;2000&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lwFLa/btsDGq1YBwL/6tqOVmkumeopy4emkY1ve1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lwFLa/btsDGq1YBwL/6tqOVmkumeopy4emkY1ve1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lwFLa/btsDGq1YBwL/6tqOVmkumeopy4emkY1ve1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlwFLa%2FbtsDGq1YBwL%2F6tqOVmkumeopy4emkY1ve1%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;1274&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1274&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;2000&quot; data-origin-height=&quot;1177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/skzz4/btsDGk8xl4N/BsTPRBUkPfagr5kNPeHTFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/skzz4/btsDGk8xl4N/BsTPRBUkPfagr5kNPeHTFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/skzz4/btsDGk8xl4N/BsTPRBUkPfagr5kNPeHTFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fskzz4%2FbtsDGk8xl4N%2FBsTPRBUkPfagr5kNPeHTFK%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;1177&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1177&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;2000&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QscSG/btsDKxZlmNU/c8A1Sp8VpXBxU6FyGBrQm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QscSG/btsDKxZlmNU/c8A1Sp8VpXBxU6FyGBrQm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QscSG/btsDKxZlmNU/c8A1Sp8VpXBxU6FyGBrQm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQscSG%2FbtsDKxZlmNU%2Fc8A1Sp8VpXBxU6FyGBrQm0%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;824&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Lucene 기반의 오픈소스 검색 엔진이며, 사이즈가 큰 데이터 검색에 용이한 특징을 가지고 있으나 검색 속도와 색인 주기가 Elasticsearch에 비해 느리다.&lt;/li&gt;
&lt;li&gt;문서 원문 검색과 같은 프로젝트에서 주로 사용하며, 최근에는 다른 검색 엔진에 비해 많이 사용되고 있지 않은 것으로 보인다. (677 stars)&lt;/li&gt;
&lt;li&gt;Elasticsearch가 머신러닝 기능은 유료인 반면에, Solr는 머신러닝 기능을 무료로 사용 가능하다는 것 이외에는 큰 메리트가 없는 것으로 보인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단에서 말한 것처럼 간단히 살펴보았을 때는 레퍼런스가 많고 커스터마이징을 많이 할 수 있는 &lt;code&gt;elasticsearch&lt;/code&gt;와 다양한 기능을 제공하고 구성하거나 사용하기 쉬운 &lt;code&gt;meilisearch&lt;/code&gt; 사이에서 고민하면 될 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 elasticsearch에서 제공하는 기능으로 우리가 구축해야 할 검색 시스템이 충분히 구현 가능하다면 ES를 사용하는 것이 베스트일 것으로 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 typesense, sonic도 고려해볼 수 있으나 Elasticsearch와 meilisearch에 비해 사용해야 할 이유를 찾기 어려워 제외했다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;ElasticSearch란?&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;2005&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mM3Wc/btsDGJtq42p/raknqPDdTRjkafxd3Vh6m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mM3Wc/btsDGJtq42p/raknqPDdTRjkafxd3Vh6m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mM3Wc/btsDGJtq42p/raknqPDdTRjkafxd3Vh6m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmM3Wc%2FbtsDGJtq42p%2FraknqPDdTRjkafxd3Vh6m1%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;2005&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2005&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2023-02 기준) ElasticSearch는 DB 랭킹의 8위를 차지하고 있으며, 검색 엔진으로서는 Stars 기준 1위의 오픈소스 프로젝트이다. (명확히는 2021년부터 오픈소스는 아니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Lucene을 기반으로 구축되었으며 Lucene의 검색 엔진에 분산형 클러스터링, RESTful API의 추가 기능을 제공한다. 또한 ELK Stack(Elasticsearch, Logstash, Kibana)을 통해 데이터 수집부터 검색 엔진, 결과 분석과 시각화까지 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS에서는 데이터를 테이블 형태로 저장하지만, 검색 엔진에서는 &lt;b&gt;Inverted index&lt;/b&gt;라는 구조로 저장하여 비정형화된 텍스트 검색에 특화되어 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RDBMS vs 검색 엔진&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;1454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8SowP/btsDNrqNd2G/oCDRBWBsHJE4LsCsW423n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8SowP/btsDNrqNd2G/oCDRBWBsHJE4LsCsW423n0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8SowP/btsDNrqNd2G/oCDRBWBsHJE4LsCsW423n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8SowP%2FbtsDNrqNd2G%2FoCDRBWBsHJE4LsCsW423n0%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;1454&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;height: 120px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;&amp;nbsp;&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;RDBMS&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;검색엔진&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;데이터 저장 방식&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;정규화&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;역정규화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;전문 검색 속도&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;느림&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;의미 검색&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;불가능&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Join&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;가능&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;수정 / 삭제&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;빠름&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;느림&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ELK Stack (Elastic Stack)&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;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0o4o/btsDGqt9j2M/zy2MaKOlMvLO2fhofVW8vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0o4o/btsDGqt9j2M/zy2MaKOlMvLO2fhofVW8vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0o4o/btsDGqt9j2M/zy2MaKOlMvLO2fhofVW8vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0o4o%2FbtsDGqt9j2M%2Fzy2MaKOlMvLO2fhofVW8vK%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;884&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Beats(경량 데이터 수집기)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 소스의 데이터를 수집하는 역할이며, 데이터 소스의 종류에 따라 다양한 beats를 설치하여 사용하는 형태이다.&lt;/li&gt;
&lt;li&gt;e.g. Filebeat(파일 내용 수집), Auditbeat(감사 데이터 수집), Metriceat(프로세스 상태 수집)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Logstash(데이터 집계, 변환)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그 데이터, RDBMS, 이벤트 데이터 등의 다양한 소스에서 데이터를 수집하고 가공한 뒤 다양한 목적지로 전송하는 기능을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Elasticsearch(검색 엔진)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apache Lucene을 기반으로 하는 오픈소스 분산 검색 및 분석 엔진&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Kibana(시각화&amp;amp;분석 도구)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elasticsearch의 데이터를 검색, 분석, 시각화(+대시보드 구성)하는 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;Log 모니터링, Metrics 대시보드, APM 등의 기능과 ML 관련 기능(유료)도 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>스터디/검색 엔진</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/39</guid>
      <comments>https://right1203.tistory.com/39#entry39comment</comments>
      <pubDate>Sun, 21 Jan 2024 14:29:21 +0900</pubDate>
    </item>
    <item>
      <title>[DevOps] Jupyterhub에서 Copilot&amp;amp;Copilot Chat 사용하기</title>
      <link>https://right1203.tistory.com/38</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; 굳이 Copilot을 주피터에서 사용해야 하나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 최근 Github Copilot Chat이 베타 버전으로 출시되었는데, 잠시 사용해보니 나쁘지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그런데 현재는 베타 버전이어서 VSCode에서만 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-  현재는 개발 부서에서 일을 하고 있긴 하지만, 분석을 할 일이 종종 있다. 그런데 다루는 데이터 크기가 워낙 크다보니 로컬에서는 어렵고, 회사 내의 Spark 클러스터 자원을 써야만 분석이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Spark 클러스터에 붙으려면 사내 k8s 내에서만 접근이 가능하기 때문에 팀 내 주피터허브를 만들어두고 이걸로 주로 분석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 분석을 할 때 코파일럿 채팅은 커녕 코파일럿도 없이 쌩으로 jupyterlab에서 분석을 하는데  코파일럿이 있다 없으니 참 불편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그래서 Jupyterhub에서 코파일럿과 코파일럿 채팅을 사용해보려고 여러 삽질을 해봤고, 결국 성공했다.&amp;nbsp;&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;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Jupyterhub 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 자세히 다루진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알아서 환경에 따라 잘 설치하면 되는데, 나 같은 경우는 Helm을 이용해서 설치했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://z2jh.jupyter.org/en/stable/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://z2jh.jupyter.org/en/stable/&lt;/a&gt;를 참고하면 되고, &lt;a href=&quot;https://hub.jupyter.org/helm-chart/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;버전&lt;/a&gt;은 크게 상관 없으니 k8s 버전에 따라 최신 버전을 쓰든 조금 오래된 버전을 쓰든 하자.&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;2. Jupyterlab 이미지 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jupyterhub의 개념에서 각각의 유저는 자기만의 jupyter 파드(Pod)를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 single-user라고 부르는데, 어떤 이미지로 jupyter 파드를 띄울 것인지는 본인이 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;values.yaml에서 아래와 같은 설정인데, 코파일럿 설정을 위해서는 이 부분을 건드려야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1703409062038&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;singleuser:
  image:
    name: myjupyter-image
    tag: version&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;그러면 myjupyter-image를  Dockerfile을 통해 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 아래 코드는 일부 발췌한거기 때문에 안 돌아갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Base 이미지를 사내 환경이 설정된 이미지를 사용했기 때문에 all-spark-notebook으로 동작하는지 알 수 없다.&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;2.1. code-server 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code-server는 vscode의 오픈소스 버전이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coder라는 곳에서 오픈소스 + 상용 솔루션을 같이 만드는 것 같은데, 베이스가 되는 code-server도 같이 제공해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 coder에서 제공하는 별개의 솔루션도 있는데 OSS 버전도 제공하니 그걸 쓰는 것도 좋을 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703410129024&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이 부분은 본인이 필요한 베이스 이미지를 사용하면 된다.
FROM jupyter/all-spark-notebook

# Install VS Code server and extensions
# code-server를 다른 곳에 복사해뒀다가 entrypoint에서 다시 가져와야함..
## Install code-server
ARG CODE_SERVER_VERSION=4.19.1

RUN mkdir /opt/code-server
COPY jupyter_resources/GitHub.copilot-1.143.601.vsix jupyter_resources/GitHub.copilot-chat-0.12.2023121501.vsix /opt/code-server/

# copilot과 copilot-chat을 미리 설치
# copilot-chat이 현재(2023-12-19) 최신 버전의 code-server와 호환이 안 되서, vsix 압축 해제 후 호환성 변경 후 다시 압축하는 형태로 설치
# https://github.com/coder/code-server/discussions/5063#discussioncomment-7847930
RUN cd /opt/code-server \
  &amp;amp;&amp;amp; curl -sL https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-$(dpkg --print-architecture).tar.gz | tar zxf - --no-same-owner --strip-components=1 \
  &amp;amp;&amp;amp; curl -sL https://upload.wikimedia.org/wikipedia/commons/9/9a/Visual_Studio_Code_1.35_icon.svg -o vscode.svg \
  ## Install code-server extensions
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-python.python \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension christian-kohler.path-intellisense \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension redhat.vscode-yaml \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension grapecity.gc-excelviewer \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension oderwat.indent-rainbow \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mechatroner.rainbow-csv \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension vscode-icons-team.vscode-icons \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-toolsai.jupyter \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension /opt/code-server/GitHub.copilot-1.143.601.vsix \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension /opt/code-server/GitHub.copilot-chat-0.12.2023121501.vsix \
  ## 아래 명령어를 추가해야 code-server terminal에서 pyenv를 정상적으로 사용 가능
  &amp;amp;&amp;amp; echo 'eval &quot;$(pyenv init -)&quot;' &amp;gt;&amp;gt; ${HOME}/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든, 위 코드는 code-server를 다운로드 받아 설치하는 과정이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CODE_SERVER 버전을 명시하고, 코파일럿과 코파일럿 채팅 익스텐션 파일을 미리 받아두고 옮겨서 설치한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; 이 두 가지만 굳이 이렇게 설치하는 이유는, code-server에 공식적으로 제공하는 익스텐션이 아니기 때문에 코파일럿 채팅 익스텐션의 버전 호환성이 안 맞기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://marketplace.visualstudio.com/search?term=copilot&amp;amp;target=VSCode&amp;amp;category=All%20categories&amp;amp;sortBy=Relevance&quot;&gt;VSCODE Marketplace&lt;/a&gt;에서 미리 다운로드 받은 뒤 unzip으로 압축을 해제하고 package.json 파일의 vscode 버전이 1.86 이상으로 되어있는데 1.84 이상으로 수정한다. (Code Server 4.19.1이 vscode 1.84.1 버전을 내부적으로 사용하고 있다)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 다시 zip으로 압축하고 이름만 vsix로 수정하면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정이 귀찮으면 미리 수정해둔 아래 파일을 다운로드 받아도 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/ttJin/btsCBMpLiTN/M5TbgkGlsVau00TwpSoN4K/GitHub.copilot-1.143.601.vsix?attach=1&amp;amp;knm=tfile.vsix&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;GitHub.copilot-1.143.601.vsix&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;5.64MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/XRiMQ/btsCw98b2P0/HGjcGN5Dgb3ZqUs3VG0WKK/GitHub.copilot-chat-0.12.2023121501.vsix?attach=1&amp;amp;knm=tfile.vsix&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;GitHub.copilot-chat-0.12.2023121501.vsix&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;2.74MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여튼 익스텐션은 필수적인 것들을 미리 설치해두는데, 당연히 필요에 따라 제외하거나 추가해도 무방하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 하면 이미지에 code-server 자체는 설치를 했는데, jupyterlab이 띄워질 때 같이 실행되도록 설정도 해야한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.2. jupyterlab 내에 code-server 실행 경로 아이콘 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jupyterlab의 메인 페이지에 code-server 실행 아이콘을 만들어두고, 클릭하면 vscode로 진입할 수 있도록 만들려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;1246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCOWd9/btsCzRrrtxb/iLzsx0X14QKqpyV5qdCtO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCOWd9/btsCzRrrtxb/iLzsx0X14QKqpyV5qdCtO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCOWd9/btsCzRrrtxb/iLzsx0X14QKqpyV5qdCtO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCOWd9%2FbtsCzRrrtxb%2FiLzsx0X14QKqpyV5qdCtO0%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;2128&quot; height=&quot;1246&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;1246&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;이러기 위해서는 jupyter_server_config.py를 수정해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1703410835238&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# jupyter_server_config.py
import os
import shutil

# Default 커널 제거
c.KernelSpecManager.ensure_native_kernel = False
c.ContentsManager.allow_hidden=True

def _codeserver_command(port):
    full_path = shutil.which(&quot;code-server&quot;)
    if not full_path:
        raise FileNotFoundError(&quot;Can not find code-server in $PATH&quot;)
    work_dir = os.getenv(&quot;CODE_WORKDIR&quot;, None)
    if work_dir is None:
        work_dir = os.getenv(&quot;HOME&quot;, &quot;.&quot;)
    elif os.path.isdir(work_dir) is False:
        os.mkdir(work_dir)
    data_dir = os.getenv(&quot;CODE_USER_DATA_DIR&quot;, &quot;&quot;)
    if data_dir != &quot;&quot;:
        data_dir = &quot;--user-data-dir=&quot; + str(data_dir)
    extensions_dir = os.getenv(&quot;CODE_EXTENSIONS_DIR&quot;, &quot;&quot;)
    if extensions_dir != &quot;&quot;:
        extensions_dir = &quot;--extensions-dir=&quot; + str(extensions_dir)

    return [
        full_path,
        &quot;--bind-addr=0.0.0.0:&quot; + str(port),
        &quot;--auth&quot;,
        &quot;none&quot;,
        data_dir,
        extensions_dir,
        work_dir,
    ]

c.ServerProxy.servers = {
    &quot;code-server&quot;: {
        &quot;command&quot;: _codeserver_command,
        &quot;timeout&quot;: 20,
        &quot;launcher_entry&quot;: {
            &quot;title&quot;: &quot;VSCode Server&quot;,
            &quot;icon_path&quot;: &quot;/opt/code-server/vscode.svg&quot;
        },
        &quot;new_browser_tab&quot;: True
    }
}&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;코드는 code_server를 환경변수들을 조합해서 실행 명령어를 만들고, c.ServerProxy.servers를 이용해서 code-server 아이콘을 등록하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 Dockerfile에서 jupyter가 인식할 수 있도록 복사해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703411009883&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Jupyter에 code-server 실행 아이콘 추가
# jupyter_server_config를 미리 정의해두고 jupyter에서 설정한다.
ENV NOTEBOOK_HOME=/home/user

COPY jupyter_resources/jupyter_server_config.py /opt/home/jupyter_resources/jupyter_server_config.py
RUN mkdir -p $NOTEBOOK_HOME \
  &amp;amp;&amp;amp; sudo chown -R app:app $NOTEBOOK_HOME \
  &amp;amp;&amp;amp; echo &quot;c.ServerApp.root_dir = '$NOTEBOOK_HOME'&quot; &amp;gt;&amp;gt; $HOME/.jupyter/jupyter_server_config.py \
  &amp;amp;&amp;amp; cat /opt/home/jupyter_resources/jupyter_server_config.py &amp;gt;&amp;gt; $HOME/.jupyter/jupyter_server_config.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 모든 설정이 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3. Dockerfile 최종&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 조각난 Dockerfile을 합쳐보면 이런 형태이다.&lt;/p&gt;
&lt;pre id=&quot;code_1703409180050&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이 부분은 본인이 필요한 베이스 이미지를 사용하면 된다.
FROM jupyter/all-spark-notebook

# Install VS Code server and extensions
# code-server를 다른 곳에 복사해뒀다가 entrypoint에서 다시 가져와야함..
## Install code-server
ARG CODE_SERVER_VERSION=4.19.1

RUN mkdir /opt/code-server
COPY jupyter_resources/GitHub.copilot-1.143.601.vsix jupyter_resources/GitHub.copilot-chat-0.12.2023121501.vsix /opt/code-server/

# copilot과 copilot-chat을 미리 설치
# copilot-chat이 현재(2023-12-19) 최신 버전의 code-server와 호환이 안 되서, vsix 압축 해제 후 호환성 변경 후 다시 압축하는 형태로 설치
# https://github.com/coder/code-server/discussions/5063#discussioncomment-7847930
RUN cd /opt/code-server \
  &amp;amp;&amp;amp; curl -sL https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-$(dpkg --print-architecture).tar.gz | tar zxf - --no-same-owner --strip-components=1 \
  &amp;amp;&amp;amp; curl -sL https://upload.wikimedia.org/wikipedia/commons/9/9a/Visual_Studio_Code_1.35_icon.svg -o vscode.svg \
  ## Install code-server extensions
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-python.python \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension christian-kohler.path-intellisense \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension redhat.vscode-yaml \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension grapecity.gc-excelviewer \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension oderwat.indent-rainbow \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mechatroner.rainbow-csv \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension vscode-icons-team.vscode-icons \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-toolsai.jupyter \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension /opt/code-server/GitHub.copilot-1.143.601.vsix \
  &amp;amp;&amp;amp; code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension /opt/code-server/GitHub.copilot-chat-0.12.2023121501.vsix \
  ## 아래 명령어를 추가해야 code-server terminal에서 pyenv를 정상적으로 사용 가능
  &amp;amp;&amp;amp; echo 'eval &quot;$(pyenv init -)&quot;' &amp;gt;&amp;gt; ${HOME}/.bashrc

# 한글폰트
ENV MATPLOTLIBRC=/opt/home/matplotlibrc
RUN wget https://intwg.kakaocdn.net/localsearch_common/for_docker_build/NanumFont_TTF_ALL.zip &amp;amp;&amp;amp; \
  sudo unzip NanumFont_TTF_ALL.zip -d /usr/share/fonts/NanumFont &amp;amp;&amp;amp; \
  rm NanumFont_TTF_ALL.zip
COPY matplotlibrc $MATPLOTLIBRC

# Jupyter에 code-server 실행 아이콘 추가
# jupyter_server_config를 미리 정의해두고 jupyter에서 설정한다.
ENV NOTEBOOK_HOME=/home/user

COPY jupyter_resources/jupyter_server_config.py /opt/home/jupyter_resources/jupyter_server_config.py
RUN mkdir -p $NOTEBOOK_HOME \
  &amp;amp;&amp;amp; sudo chown -R app:app $NOTEBOOK_HOME \
  &amp;amp;&amp;amp; echo &quot;c.ServerApp.root_dir = '$NOTEBOOK_HOME'&quot; &amp;gt;&amp;gt; $HOME/.jupyter/jupyter_server_config.py \
  &amp;amp;&amp;amp; cat /opt/home/jupyter_resources/jupyter_server_config.py &amp;gt;&amp;gt; $HOME/.jupyter/jupyter_server_config.py

ENTRYPOINT [&quot;/opt/home/entrypoint.sh&quot;]

EXPOSE 8888

WORKDIR $HOME&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸로 Docker 이미지를 만들고, helm의 single-user 이미지에 넣어주면 이제 코파일럿을 사용할 수 있다. (dockerhub든 별개의 이미지 레지스트리이든 이미지를 올리긴 해야한다)&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;3. 결과물&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서의 스크린샷처럼 code-server 아이콘을 클릭하면 웹 형태의 VSCode가 실행된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3512&quot; data-origin-height=&quot;1796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMbpnG/btsCywVN5iu/hy7nIVCMMTpOiHDlryEtd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMbpnG/btsCywVN5iu/hy7nIVCMMTpOiHDlryEtd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMbpnG/btsCywVN5iu/hy7nIVCMMTpOiHDlryEtd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMbpnG%2FbtsCywVN5iu%2Fhy7nIVCMMTpOiHDlryEtd0%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;3512&quot; height=&quot;1796&quot; data-origin-width=&quot;3512&quot; data-origin-height=&quot;1796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github 로그인을 해주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 github 로그인이 안 된다면, 프록시 이슈일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사라면 proxy 환경변수 설정을 해주거나 (http_proxy, https_proxy, no_proxy 등) 사내 보안팀에 &lt;a href=&quot;https://api.githubcopilot.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://api.githubcopilot.com/&lt;/a&gt; 를 proxy 오픈해달라고 요청해야 할 수 있다.&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;3414&quot; data-origin-height=&quot;1752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biUnZH/btsCw6wRpc5/H6TMKqHLqWEPgnJxpQQq81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biUnZH/btsCw6wRpc5/H6TMKqHLqWEPgnJxpQQq81/img.png&quot; data-alt=&quot;코파일럿 채팅을 사용하는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biUnZH/btsCw6wRpc5/H6TMKqHLqWEPgnJxpQQq81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiUnZH%2FbtsCw6wRpc5%2FH6TMKqHLqWEPgnJxpQQq81%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;3414&quot; height=&quot;1752&quot; data-origin-width=&quot;3414&quot; data-origin-height=&quot;1752&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코파일럿 채팅을 사용하는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코파일럿 Tip&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소개하는 김에 코파일럿을 사용하면서 제일 중요했던 팁 두 가지만 공유 해보자면..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 주석 달아두기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 -&amp;gt; 주석의 순서가 아니라, 주석 -&amp;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;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터 붙여넣기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 구현하기 전에 관련 데이터가 있다면 무조건 데이터를 주석으로 붙여놓자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 주석으로 달아놓고 코드를 짜기 시작하면, 그걸 고려해서 코파일럿이 제안해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/07Y9R/btsCzEZ4TvV/32taktu6roYZ1v3uSC0qsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/07Y9R/btsCzEZ4TvV/32taktu6roYZ1v3uSC0qsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/07Y9R/btsCzEZ4TvV/32taktu6roYZ1v3uSC0qsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F07Y9R%2FbtsCzEZ4TvV%2F32taktu6roYZ1v3uSC0qsk%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;1502&quot; height=&quot;1030&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스터디/DevOps</category>
      <category>github copilot</category>
      <category>jupyterhub</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/38</guid>
      <comments>https://right1203.tistory.com/38#entry38comment</comments>
      <pubDate>Sun, 24 Dec 2023 18:57:30 +0900</pubDate>
    </item>
    <item>
      <title>[사이드 프로젝트] 데이터 엔지니어의 백엔드 생존기</title>
      <link>https://right1203.tistory.com/37</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc3se3/btsBFFMQ03X/mieUt6RRQMqLfNpmVezPL0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc3se3/btsBFFMQ03X/mieUt6RRQMqLfNpmVezPL0/img.jpg&quot; data-alt=&quot;직무 리셋&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc3se3/btsBFFMQ03X/mieUt6RRQMqLfNpmVezPL0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc3se3%2FbtsBFFMQ03X%2FmieUt6RRQMqLfNpmVezPL0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;381&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;- 올해 3월부터 데이터 조직 -&amp;gt; 서비스 조직으로 팀 이동을 하면서 elasticsearch(이하 ES)를 이용해 검색 엔진을 개발하는 신규 프로젝트를 맡게 되었다. ES와 데이터 파이프라인, API 서버(Java, Spring boot)를 구성해야만 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 분석 2년, 엔지니어링 1년 반 정도 업무를 진행한 입장에서 검색 엔진 용도의 ES는 물론이고 Java는 자료구조 수업에서만 써봤기에 다시 신입으로 돌아간 기분이었다. (웬만한 신입은 Spring에 능숙한 상태로 들어올 듯)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 백엔드 개발에 빠르게 익숙해지기 위해 마침 사내에서 지원해 주는 &lt;a href=&quot;https://edu.nextstep.camp/c/8fWRxNWU/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TDD, 클린코드 with Java&lt;/a&gt; 교육을 무작정 수강했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 2개월 정도 교육이 진행됐는데, 솔직히 따라가기 힘들었다. Java와 백엔드를 전혀 모르는데 클린코드라니.. TDD라니.. 리팩토링이라니..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어쨌든 교육이 끝날 즈음에 Spring 강의도 정말 기초만 들었는데, 강의만 듣는 건 내 스타일이 아니라서 실습 목적으로&amp;nbsp;&lt;a href=&quot;https://bside.best/&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;h2 data-ke-size=&quot;size26&quot;&gt;사이드 프로젝트 진행기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비사이드에서는 돈을 내면 사이드 프로젝트를 위한 현직자 매칭과 관리를 도와준다. 총 8명으로 한 팀이 구성되는데, 기획/디자인/프론트엔드/백엔드 2명씩 구성되고 이 중 한 명이 PM 역할을 맡게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 나는 당연히 백엔드에 익숙해지고자 시작했기 때문에 백엔드로 지원했다. 데이터 엔지니어가 백엔드로 지원한 케이스는 내가 처음이라고 하더라~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비사이드를 시작할 때쯤 7월 초였는데, 검색엔진이 어느정도 구성돼서 Spring으로 API 서버까지 간단하게 구성된 상태였기에 크게 다르지 않을 줄 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 근데 서비스 백엔드와 ES를 중계해주는 API 서버는  완전히 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회원가입, 로그인, DB(JPA, QueryDSL 등), 동시성 등 신경 쓸 부분이 전혀 달랐다. 우리 팀의 프론트는 안드로이드 앱이었기 때문에 안드로이드를 전혀 몰라서 헤맸던 부분들도 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 사이드 프로젝트를 진행하면서 배웠던 부분들을 기록해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kp5rg/btsBCY0HFEi/7fHjr96yzjWu6IyAGRq800/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kp5rg/btsBCY0HFEi/7fHjr96yzjWu6IyAGRq800/img.png&quot; data-alt=&quot;GPT와 함께라면 내 일자리 말고는 두려울 것이 없다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kp5rg/btsBCY0HFEi/7fHjr96yzjWu6IyAGRq800/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKp5rg%2FbtsBCY0HFEi%2F7fHjr96yzjWu6IyAGRq800%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;710&quot; height=&quot;130&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPT와 함께라면 내 일자리 말고는 두려울 것이 없다.&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;u&gt;&lt;b&gt;GPT4와 코파일럿&lt;/b&gt;&lt;/u&gt;만 있으면 구현에서는 두려울 게 없다. 거짓말도 하고, 로직이 이상할 때는 있어도 그것만 잘 다루면 구현이 이전보다 훨씬 쉬워졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제로 나는 카카오 로그인, OAuth, JWT토큰, JPA, QueryDSL, 타임리프 같은 것들을 하나도 모르는 상태로 시작했다. 하지만 GPT와 코파일럿을 쓰면서 구현에 큰 어려움이 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이런 LLM 친구들도 잘 쓰는 것이 중요한 것 같다. 특히 GPT는 GPT3.5와 4의 차이가 디테일로 들어갈수록 크고, 코파일럿은 익숙해져야 효용성 체감이 확실히 된다. 예를 들어 코파일럿을 사용하게 되면 구현 -&amp;gt; 주석이 아니라 주석 -&amp;gt; 구현 순서로 해보자. 단순한 구현에서 내가 할 일은 &lt;u&gt;주석을 쓴 뒤 tab 누르는 것&lt;/u&gt;밖에 없어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 얼마 전, 본가로 가는 기차에서 코딩을 하다가 인터넷이 끊겼는데 GPT와 코파일럿이 멈추자 내 세상도 함께 멈췄다. 얼마나 내가 여기에 의존했는지 느낄 수 있었고 GPT 6~7쯤 되면 내 일자리를 지키기 위해 러다이트 운동 밖에 남은 길이 없을 것 같다.&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;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ej0DY/btsBGENoMbw/Cl4Zxhx4NsJjMNWbAAQGik/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ej0DY/btsBGENoMbw/Cl4Zxhx4NsJjMNWbAAQGik/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ej0DY/btsBGENoMbw/Cl4Zxhx4NsJjMNWbAAQGik/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Ej0DY/btsBGENoMbw/Cl4Zxhx4NsJjMNWbAAQGik/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;553&quot; height=&quot;304&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트 코드의 중요성을 정말 뼈저리게 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 초기에 Spring을 잘 모르니 테스트 코드를 어떻게 짜야할지 모르겠더라. 그래서 결국 TDD를 배웠지만 사이드 프로젝트에서는 테스트 코드를 하나도 안 짰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 근데 코드가 복잡해질수록 테스트 코드 없이 뭐가 뻑날지 모르는 채 불안감을 가지고 배포하고, 배포한 뒤에 프론트엔드 분들에 의해 버그가 발견되는 일이 반복됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그래서 기능 개발/수정할 때마다 &lt;b&gt;&lt;u&gt;직접 Postman으로 하나씩 콜을 날려보면서 배포하는데, 이 시간이 테스트 코드를 짜는 시간보다 길었을 것 같다&lt;/u&gt;. &lt;/b&gt;그리고 기능 개발에 대한 부담감이 점점 더 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제 업무에서는 최대한 테스트코드를 자세히 작성해서 테스트 커버리지가 90% 정도인데, 훨씬 큰 프로젝트인데도 기능 개발에 훨씬 부담이 덜했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;&lt;b&gt;테스트 코드 작성이 오래 걸리는 건 맞다. 기능 개발보다 테스트 코드 짜는 게 더 오래 걸리는 것도 흔한 것 같다. 그런데 테스트 코드는 꼭 짜면서 개발하자&lt;/b&gt;&lt;/u&gt;. 개발하는 도메인에 대한 이해도가 높다면 TDD를 적용해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TDD 교육에서는&amp;nbsp;&lt;b&gt;도메인에 익숙하지 않다면 개발한 후에 개발한 코드를 날려버리고 TDD로 다시 짜보는 것도 좋다고 했다.&lt;/b&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;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kI8nl/btsBHp3nnIZ/VeKK7uHOExqKKbBPCjIBN1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kI8nl/btsBHp3nnIZ/VeKK7uHOExqKKbBPCjIBN1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kI8nl/btsBHp3nnIZ/VeKK7uHOExqKKbBPCjIBN1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkI8nl%2FbtsBHp3nnIZ%2FVeKK7uHOExqKKbBPCjIBN1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;531&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 백엔드 개발을 공부하면서 여러 원칙을 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TDD, DDD, 디자인 패턴, SOLID, 소프트웨어 개발 3대 원칙, 클린코드 등 많은 개발 원칙들을 다 신경 쓰다 보니 개발을 못 하겠더라.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 개발 원칙을 다 지키려다 보니 테스트 코드를 짜는 것이 너무 어려워졌다. 예를 들어 테스트 코드가 기능 구현에 영향을 주지 않기 위해 클래스 접근자를 private -&amp;gt; protected로 안 바꾸고 테스트 코드를 짜려고 하니 리플렉션을 쓰는 등 너무 어려워졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그리고 세터를 하나도 안 쓰려니 코드가 더 더러워졌다. 세터를 안 쓰는 이유는 단순 setter는 비즈니스 로직의 의도가 드러나지 않는 문제와 일관성 유지에 대한 문제 때문인데 DTO에도 세터를 안 쓰는 것이 중요한가? 이 부분에 대해서 개인적으로는 아니라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이런 것처럼 &lt;u&gt;&lt;b&gt;개발 원칙을 다 따르는 게 중요하다기보다는, 그 의도를 이해하고 내 주관을 가지고 적절히 적용하는 것이 중요하다는 생각이 들었다&lt;/b&gt;&lt;/u&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인프라&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비사이드에서 NCP(Naver Cloud Platform)와 제휴를 맺어서 100만 원 크레딧을 줬다. 그래서 처음으로 NCP를 써봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;&lt;b&gt;NCP를 3개월 정도 써본 입장에서는 가격, 편의성, 서비스 다양성 측면 모두에서 AWS나 GCP에 비해 상대적으로 열세라고 느껴졌다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가격: Server 하나로 비교하면 AWS, GCP보다 저렴하지만 Kubernetes, VPC나 LB 등의 구성, DB 서비스 등을 포함하면 오히려 비싼 것 같았다. 특히 Kubernetes는 마스터 노드와 VPC 구성 하나하나에 비용이 나가기 때문에 GCP의 오토파일럿 모드나 Vultr에 비해 2배 이상 비싸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 편의성: 대부분의 프레임워크, 라이브러리에서는 AWS, GCP는 기본으로 지원해 줘도 NCP를 지원해 주는 곳은 못 봤다. 대부분 커스터마이징을 하거나 직접 구현해야 했다. 그리고 문서화 측면에서도 AWS는 유저들이 문서화를 대신 다 해줬고, GCP는 문서가 엄청 친절하고 자세하다. 그런데 NCP는 기본적인 문서화만 되어 있다. 사용 편의성 측면에서도 딱히 GCP에 비해 편하다고 느껴지진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스 다양성: AWS는 정말 온갖 서비스가 다 있다. 필요하면 다 가져다 붙이면 되기 때문에 인프라에 종속성이 생기지만 정말 편하긴 한 것 같다.  NCP는 당연한 이야기지만 AWS나 GCP에 비해 서비스 종류가 많지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그래서 다음 사이드 프로젝트를 진행할 때는 아마 무료 크레딧을 주는 GCP를 쓰거나 AWS 프리티어로 시작하지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그리고 사이드 프로젝트로 실제 돈이 나가는 것이 애매하다 보니 100만 원 크레딧을 최대한 아껴 쓰려고 최대한 간단히 구성했다. 회사에서는 쿠버네티스 기반으로 펑펑 쓰다 보니 가난하게 구성한 것이 처음이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Server 한 대와 DB 한 대, 도메인&amp;amp;LB로 구성해서 쓰고 있다. 그러다 보니 docker-compose와 github actions를 처음 써보게 됐는데, 'github actions 진짜 좋다!'와 '쿠버네티스가 진짜 좋았구나..'를 느꼈다. 깃허브 액션은 참 편했고, docker-compose는 쿠버네티스에 비해 서버 운영에 신경 써야 되는 게 너무 많았다.&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;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/niD6b/btsBGGqVXI1/ENnaRarf7tPD3cdQY0W3Qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/niD6b/btsBGGqVXI1/ENnaRarf7tPD3cdQY0W3Qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/niD6b/btsBGGqVXI1/ENnaRarf7tPD3cdQY0W3Qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FniD6b%2FbtsBGGqVXI1%2FENnaRarf7tPD3cdQY0W3Qk%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;455&quot; height=&quot;566&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 요약해 보자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사이드 프로젝트로 만든 안드로이드 앱은 곧 출시한다. (초기 계획보다 1~2개월 늦어짐..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LLM이 우리의 일자리를 뺏기 전에 최대한 친해지자. 매달 돈이 나가더라도 LLM 기반 개발에 익숙해지면 생산성이 크게 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트 코드는 진짜 중요하다. 안 짜면 내 불안감도 건강에 해롭고 결국 수동 테스트하느라 시간마저 더 들어간다. TDD를 적용하는 것에는 도메인 이해도가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 개발 원칙에 집착하지 말자. 개발 원칙의 의도를 이해하고 내 상황과 주관에 맞게 적절히 활용하자. 개발 원칙은 헌법이 아니다. 하지만 개발 원칙으로 여겨지는 데에 이유가 있는 것도 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인프라는 AWS, GCP가 좋은 것 같다. 편의성은 GCP, 표준은 AWS, 가격은 Vultr..(아는 사람이 별로 없을 듯하다. 진짜 싸긴 하다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 쿠버네티스 조아&lt;/p&gt;</description>
      <category>사이드 프로젝트</category>
      <category>java</category>
      <category>ncp</category>
      <category>spring</category>
      <category>개발원칙</category>
      <category>백엔드</category>
      <category>사이드프로젝트</category>
      <category>인프라</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/37</guid>
      <comments>https://right1203.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 10 Dec 2023 19:44:47 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 직무가 뭐에요?</title>
      <link>https://right1203.tistory.com/36</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;직무가 뭐에요? 무슨 일해요?&lt;br&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;누군가를 처음 만났을 때 흔히들 묻는 말인데요.&lt;br&gt;저에게 대답하기 망설여지는 말이기도 합니다.&lt;br&gt;저도 제 직무가 뭔지 모르겠거든요.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot;&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;보안 전문가&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;시작은 보안 전문가(화이트해커)였습니다.&lt;br&gt;아, 보안 전문가 일을 했다는 건 아니구요.&lt;br&gt;처음 목표로 했던 직업이 보안 전문가였습니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XnpzI/btsAalQF9UU/diVniULBmmK3k42GN6Tl80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XnpzI/btsAalQF9UU/diVniULBmmK3k42GN6Tl80/img.png&quot; data-alt=&quot;조선일보 전광판 중학생한테 다털렸죠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XnpzI/btsAalQF9UU/diVniULBmmK3k42GN6Tl80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXnpzI%2FbtsAalQF9UU%2FdiVniULBmmK3k42GN6Tl80%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;460&quot; height=&quot;392&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;조선일보 전광판 중학생한테 다털렸죠&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;때는 바야흐로 군인 시절.&lt;br&gt;반복되는 사무 업무를 피하고자 밤을 새우며 엑셀을 공부해서 마침내 자동화로 반복 업무를 없앴던 경험이 있었습니다.&lt;br&gt;자동화 엑셀 파일이 제 자식 같기도 하고 보물 1호 같은 마음에, 기어코 개발을 공부해서 무언가를 만들어내는 사람이 되고 싶다는 나쁜 마음을 먹고 말았습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그 첫 시작이 가장 멋있어보였던 화이트해커였던거죠.&lt;br&gt;&quot;해커라니! 그것도 해커를 잡는 해커라니!&quot; 같은 가벼운 마음이었던 것 같습니다.&lt;br&gt;마음이야 어쨌든 기초부터 다져야겠다는 마음에 기초 관련 서적들을 읽기 시작했습니다.&lt;br&gt;문제는 기초를 배우는 건 좋지만, 주변에 관련해서 물어볼 사람이 1도 없었던 나머지 너무 근본으로 가버렸던 건데요.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r1f9s/btsAfC4FCoC/L8Trw6RMyCUFeZKaUG6dg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r1f9s/btsAfC4FCoC/L8Trw6RMyCUFeZKaUG6dg0/img.png&quot; data-alt=&quot;뇌를 너무 자극해버린 나머지 2페이지마다 졸았던 책&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r1f9s/btsAfC4FCoC/L8Trw6RMyCUFeZKaUG6dg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr1f9s%2FbtsAfC4FCoC%2FL8Trw6RMyCUFeZKaUG6dg0%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;400&quot; height=&quot;545&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;뇌를 너무 자극해버린 나머지 2페이지마다 졸았던 책&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;운영체제(공룡책)&lt;/li&gt;&lt;li&gt;시스코 네트워크&lt;/li&gt;&lt;li&gt;칼리 리눅스&lt;/li&gt;&lt;li&gt;뇌를 자극하는 윈도우즈 시스템 프로그래밍.. 등등&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;프로그래밍 지식이 전무하던, 심지어 코딩이라고는 C로 Hello World나 피라미드 찍기를 손코딩하던 시절(*을 손으로 한땀한땀 그렸습니다) 프로세스 통신, 뮤텍스 같은 지식이 나오는 책을 읽고 있었던 것이죠.&lt;br&gt;그래도 의지는 대단했던 것이 2년 동안 거의 매일 책을 읽기는 했던 것 같습니다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 현재 머릿 속에 남아있는 지식은 얼마 없습니다.&lt;br&gt;지식이 없는 상태에서 전혀 모르는 내용을 들으니 기억이 날 리가 없겠죠.&lt;br&gt;다른 분들은 ChatGPT에게라도 공부 방향성을 물어봅시다  &lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;2년이 지나 전역한 직후, 기초를 쌓았으니(실제로 쌓지는 못했지만요) 드디어 해킹을 배워야지! 싶어서 해킹 동아리에 어렵게 들어갔는데요.&lt;br&gt;그리고 2개월 만에 탈퇴하고 꿈을 접게 되었습니다..&lt;br&gt;&amp;nbsp;&lt;br&gt;공부나 취업 방향성을 묻고자 해킹 동아리 선배, 컴공 교수님, 보안 스타트업 대표님 등 열심히 연락해서 면담을 했던 결과,&lt;br&gt;모든 분들이 보안 업계의 소수 1%만 살아남는 험난한 현실을 알려주며 포기하는 것을 권유해주셨기 때문인데요.&lt;br&gt;지금 생각하면 준비한 기간에 비해 너무 빨리 포기한 것이 아닌가 싶지만, 그 때는 실패에 대한 두려움이 컸던 것 같습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;데이터 분석가&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;나의 지난 2년이 사라진 것 같은 허망한 마음에 전공(경영학과) 교수님들을 찾아가서 열심히 면담을 했었습니다.&lt;br&gt;그 중 MIS(경영 정보 시스템)에서 데이터 분석을 전공으로 하시는 교수님께서 코딩을 필요로 하는 데이터 분석은 어떻냐고 제안을 주셨고,&lt;br&gt;저는 조언을 받아들여 분석가의 길로 전향하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;신생 데이터 분석 동아리에 들어가서 같이 운영과 스터디도 하고,&lt;br&gt;ML 기법을 이해하기 위해 통계/수학을 공부하기도 하며,&lt;br&gt;데이터 분석 대회나 공모전에도 열심히 참가했던 나날들을 2~3년 보냈던 것 같습니다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;여기서도 공부 방향성에 대한 아쉬움은 있었습니다.&lt;br&gt;전통적인 ML보다는 AI/비즈니스 전략/오픈소스 기술 등에 좀 더 집중했다면 어땠을까 하는 아쉬움이요.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;졸업 시즌이 다가오고 취업을 해야할 때쯤, 공부해오던 일에 현타를 느꼈던 것 같습니다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해볼 수 있는 기법들(xgboost, catboost, SVM 등)을 전부 그냥도 쓰고, 튜닝해서도 쓰고, 앙상블도 해본 다음에 성능이 0.1%라도 높은 모델이 제일 좋은 모델이다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;&quot;이런 결론을 내는게 정말 의미가 있을까? 무엇보다 내가 이 작업들에 재미를 느끼나?&quot;&lt;/b&gt;&lt;br&gt;사실 제가 부족했던 문제였지만 어쨌든 이런 현타를 느낄 때 재미로 해봤던 &lt;a href=&quot;https://right1203.tistory.com/5&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;게임 분석&lt;/span&gt;&lt;/a&gt;이 너무나 재밌었고,&lt;br&gt;좋아하는 분야인 게임 회사에 애드혹 분석을 위주로 하는 분석가로 일을 해보고 싶었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 실제로 가장 원했던 게임 회사에서 원했던 애드혹 분석을 실컷 하는 분석가로 취업했습니다!!&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LaA3j/btsAkteAsJa/NCrSOALmVnLfj0KIxCuxOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LaA3j/btsAkteAsJa/NCrSOALmVnLfj0KIxCuxOk/img.png&quot; data-alt=&quot;와... 신난다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LaA3j/btsAkteAsJa/NCrSOALmVnLfj0KIxCuxOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLaA3j%2FbtsAkteAsJa%2FNCrSOALmVnLfj0KIxCuxOk%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;517&quot; height=&quot;287&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;와... 신난다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;(배부른 소리 주의)&lt;br&gt;실제로 처음에는 너무 일이 재미있었고 행복했습니다.&lt;br&gt;그러나 일을 시작한 지 2년이 지났을 무렵, 번아웃으로 지친 저만이 남아있었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;좋은 업무 환경, 재밌고 유능한 동료, 합리적이고 신뢰해주는 상사, 좋은 복지 등 장점은 너무너무너무 많았지만..&lt;br&gt;단점은 &lt;b&gt;&quot;분석이 재미가 없다&quot;&lt;/b&gt; 하나였습니다.&lt;br&gt;왜 재미가 없냐하면 조직의 구조적인 문제, 분석이 액션으로 이어지는 경험이 적은 것, 정답이 없는 문제 등 많은 변명을 들 수 있겠지만 이유가 중요하진 않았습니다.&lt;br&gt;중요한 건 내게 지금 분석이 재미없고, 재미를 찾고자 이리저리 시도해봤지만 안 됐고, 일이 재미가 없는게 나에겐 번아웃이 올 정도의 문제였다는거죠.&lt;br&gt;(사실 지금 돌이켜보면 배가 불러도 너무 부른 소리입니다)&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;873&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WjBYs/btsAfBko8eS/zpjr0QlkQoSf2eZsjkOdF1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WjBYs/btsAfBko8eS/zpjr0QlkQoSf2eZsjkOdF1/img.jpg&quot; data-alt=&quot;돈 버는 일에 재미를 찾는 도파민 중독&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WjBYs/btsAfBko8eS/zpjr0QlkQoSf2eZsjkOdF1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWjBYs%2FbtsAfBko8eS%2Fzpjr0QlkQoSf2eZsjkOdF1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;590&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;873&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;돈 버는 일에 재미를 찾는 도파민 중독&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;데이터 엔지니어&lt;/h3&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;분석은 싫은데, 분석 커리어는 살리고 싶고&lt;br&gt;개발은 두려운데, 개발은 하고 싶고&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이런 그릇된 욕심 속에 선택한 직무가 데이터 엔지니어였습니다.&lt;br&gt;분석과 개발 두 마리 토끼를 다 잡을 수 있는 직무처럼 보였었죠.&lt;br&gt;(그게 바로 잡부였던 것을..)&lt;br&gt;&amp;nbsp;&lt;br&gt;조직 내에서 면담을 통해 데이터 엔지니어링 업무를 맡고 싶다고 의견을 제시했었고,&lt;br&gt;실제로 조직 내의 분석 환경 구성이나 분석 라이브러리 개발, Spark 튜닝 등을 진행하기도 했었습니다.&lt;br&gt;하지만 분석가로 2년 구른 사람을 처음 해보는 엔지니어링 업무만 하게 해주는 것도 자원 봉사겠죠.&lt;br&gt;데이터 엔지니어링 업무를 수행하는 것에 구조적으로도, 실력적으로도 한계를 느꼈습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;지금은 주니어라서 괜찮지만 분석가 커리어가 계속 쌓이다보면 커리어 시프트가 불가능하지 않을까 하는 두려움이 생겼고,&lt;br&gt;3개월 안에 이직 or 퇴사라는 배수의 진을 쳐놓고 열심히 준비했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그 결과 다행히 두 번의 연속 이직 끝에 현재 회사로 오게 되었는데요.&lt;br&gt;분석가로 이직했지만, 신생 데이터 조직이고 엔지니어링 업무를 필요로 한다는 것은 면접에서 들어서 기대하고 입사했었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 실제 마주한 것은 &lt;b&gt;가내수공업&lt;/b&gt;이었습니다.&lt;br&gt;팀원들은 로컬 맥북에서 수십, 수백 기가 데이터를 다루고 있었고 큰 데이터를 pandas로 다루다보니 OOM은 일상이었고, 각자 로컬에서 작업해서 합치는 형태로 작업 중이었습니다.&lt;br&gt;한 번의 데이터 추출은 심호흡이 필요할 정도로 부담스러운 작업이었구요.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBPdDD/btsAhfhnD2z/aM3RZCoy6T6s32zECgjYck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBPdDD/btsAhfhnD2z/aM3RZCoy6T6s32zECgjYck/img.png&quot; data-alt=&quot;숙제: 각자 집에서 데이터 가공해오기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBPdDD/btsAhfhnD2z/aM3RZCoy6T6s32zECgjYck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBPdDD%2FbtsAhfhnD2z%2FaM3RZCoy6T6s32zECgjYck%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;453&quot; height=&quot;470&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;숙제: 각자 집에서 데이터 가공해오기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;그래서 입사하자마자 데이터 엔지니어링 업무를 자처했고,&lt;br&gt;분석 환경 구성, 분석/데이터 핸들링 라이브러리 개발, Spark 환경 구성, 데이터 파이프라인 구성 등&lt;br&gt;1년 6개월 동안은 분석가 보다는 데이터 엔지니어 역할을 위주로 수행했던 것 같습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 어느정도 세팅이 완료되고나니 팀이 사라졌습니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B122y/btsAiDI28RY/M8XaVeSpVb901qonwOrQ8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B122y/btsAiDI28RY/M8XaVeSpVb901qonwOrQ8k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B122y/btsAiDI28RY/M8XaVeSpVb901qonwOrQ8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB122y%2FbtsAiDI28RY%2FM8XaVeSpVb901qonwOrQ8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;384&quot; height=&quot;333&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;백엔드 개발자&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이후 팀을 이동해서 현재는 거의 백엔드 개발자 업무에 가깝습니다.&lt;br&gt;데이터 엔지니어링도 조금 하고, 분석도 아주 조금 하지만요.&lt;br&gt;지도와 검색이라는 도메인에 치이고 있지만 많은 것을 배우고 있습니다.&lt;br&gt;Java와 Spring을 위주로, Elasticsearch를 분석엔진이 아닌 검색엔진으로 사용하면서요.&lt;br&gt;(좌표계가 이렇게 많은 줄 몰랐습니다. 지구를 왜 타원체로 봐..)&lt;br&gt;그리고 아주 조금의 프론트엔드 업무도 가미되어있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 백엔드 개발에 익숙해지고자 사이드 프로젝트에 백엔드로 참여해서 앱도 곧 출시 예정입니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Bb8D/btsAeCcRUJR/RF6aUYelvHYIrIxIcO0f8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Bb8D/btsAeCcRUJR/RF6aUYelvHYIrIxIcO0f8K/img.png&quot; data-alt=&quot;곧 출시합니다. 많관부 :)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Bb8D/btsAeCcRUJR/RF6aUYelvHYIrIxIcO0f8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Bb8D%2FbtsAeCcRUJR%2FRF6aUYelvHYIrIxIcO0f8K%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;334&quot; height=&quot;594&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;곧 출시합니다. 많관부 :)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;지금까지 저의 커리어를 시작 전부터 현재까지 정리해보았는데요.&lt;br&gt;결국 아직 제가 무슨 직무인지 모르겠습니다.&lt;br&gt;데이터 분석/데이터 엔지니어/백엔드(+조금의 프론트엔드)까지 더하며 더 정체 모를 잡부가 되었네요.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bneowa/btsAkjJN9m4/awbQURBc8GNqbht6o6yzSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bneowa/btsAkjJN9m4/awbQURBc8GNqbht6o6yzSK/img.png&quot; data-alt=&quot;잡부 진화...!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bneowa/btsAkjJN9m4/awbQURBc8GNqbht6o6yzSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbneowa%2FbtsAkjJN9m4%2FawbQURBc8GNqbht6o6yzSK%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;531&quot; height=&quot;770&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잡부 진화...!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;사실 지금의 상황이 싫지는 않습니다.&lt;br&gt;너무 찍먹하는 것은 커리어에 안 좋은 영향을 미칠 수도 있겠죠.&lt;br&gt;다만 아직은 새로운 것을 배우고, 새로운 업무를 하는 것이 재밌고 좋습니다.&lt;br&gt;그만 새롭고 싶을 때도 있지만요..&lt;br&gt;&lt;br&gt;긴 글 읽어주셔서 감사합니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MRWTy/btsAc5e6UKR/eQ0hlcYH5NTdMzxLeIHVD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MRWTy/btsAc5e6UKR/eQ0hlcYH5NTdMzxLeIHVD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MRWTy/btsAc5e6UKR/eQ0hlcYH5NTdMzxLeIHVD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMRWTy%2FbtsAc5e6UKR%2FeQ0hlcYH5NTdMzxLeIHVD1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;389&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>나의 이야기</category>
      <category>회고</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/36</guid>
      <comments>https://right1203.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 14 Nov 2023 00:25:09 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] #4 ReplicaSet</title>
      <link>https://right1203.tistory.com/35</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CKA 자격증 준비 과정에서 Udemy의 &lt;a href=&quot;https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Certified Kubernetes Administrator (CKA) with Practice Tests&lt;/a&gt; 강의를 수강하고 정리한 내용입니다. 대부분의 자료는 해당 강의의 자료이며, 이외 레퍼런스는 관련 내용 상단에 표기하였으니 참고 바랍니다.&lt;/blockquote&gt;
&lt;h1&gt;1. ReplicaSet이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 앱이 장애가 발생하더라도, 동일한 다른 앱은 정상 작동하여 장애가 발생하지 않은 것처럼 동작하기 위한 오브젝트가 ReplicaSet 또는 Replication Controller입니다.&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;ReplicaSet과 Replication Controller는 동일한 목적을 가지고 있지만 일부의 차이가 있는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 오브젝트의 목적은 Load Balancing과 Scaling입니다.&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;ReplicaSet과 Replication Controller는 동일한 Pod를 여러개 복제하고, 유저들이 여러 Pod 중 하나에 분배되어 접근하도록 하며(Load Balancing), 항상 정해진 수만큼 Pod가 실행 중이도록 유지합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod의 복제(replicas) 수를 조정하여 실행 중인 Pod를 줄이거나 늘릴 수도 있습니다. (Scaling)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DAQ33/btrPQg7y1Ax/RnNEi6AEjOZplRTdttLWyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DAQ33/btrPQg7y1Ax/RnNEi6AEjOZplRTdttLWyk/img.png&quot; data-alt=&quot;LoadBalancing &amp;amp;amp; Scaling&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DAQ33/btrPQg7y1Ax/RnNEi6AEjOZplRTdttLWyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDAQ33%2FbtrPQg7y1Ax%2FRnNEi6AEjOZplRTdttLWyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1866&quot; height=&quot;1192&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LoadBalancing &amp;amp; Scaling&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;2. ReplicaSet vs Replication Controller&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 오브젝트의 목적은 동일하지만 세부적인 기능에는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론을 먼저 말하자면, &lt;code&gt;ReplicaSet이 Replication Controller를 대체하는 최신 개념이기 때문에 ReplicaSet만 사용하면 됩니다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 오브젝트는 Pod를 관리할 수 있는 범위에서 차이가 있습니다.&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;Replication Controller&lt;/b&gt;: &lt;code&gt;Equality-based Selectors&lt;/code&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;e.g) environment = production&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ReplicaSet&lt;/b&gt;: &lt;code&gt;Set-based Selectors&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;In, NotIn, Exists, DoesNotExist 와 같은 연산자를 사용 가능&lt;/li&gt;
&lt;li&gt;e.g) environment in (prod, dev)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;3. Example&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Replication Controller&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: ReplicationController
metadata:
    name: myapp-rc
    labels:
        app: myapp
        type: front-end
spec:
    template: # template 아래에 Pod에 대한 내용이 담긴다.
        metadata:
            name: myapp-pod
            labels:
                app: myapp
                type: front-end
        spec:
            containers:
            - name: nginx-container
                image: nginx
    **replicas: 3**
    selector:
        app: myapp&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;ReplicaSet&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ReplicaSet은 selector 정의가 반드시 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: ReplicaSet
metadata:
    name: myapp-replicaset
    labels:
        app: myapp
        type: front-end
spec:
    template:
        metadata:
            name: myapp-pod
            labels:
                app: myapp
                type: front-end
        spec:
            containers:
            - name: nginx-container
                image: nginx
    replicas: 3
    selector: # selector를 정의하면, 특정 label의 Pod 장애를 ReplicaSet에게 맡길 수 있다.
        matchLabels:
            app: myapp

# or 
    selector:
        matchExpressions:
        - key: app
            operator: In
            values:
            - myapp
            - yourapp&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;4. Command&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReplicaSet 관련 Commands&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# ReplicaSet에 대한 설명
kubectl explain replicaset
# 생성
kubectl create replicaset replicaset-test --image=nginx --dry-run=client -o yaml &amp;gt; replicaset-test.yaml
kubectl create -f replicaset-definition.yaml
# 리스트
kubectl get replicaset # rs
# 삭제
kubectl delete replicaset myapp-replicaset
# 수정 or 업데이트
kubectl replace -f replicaset-definition.yaml
# 스케일
kubectl scale --replicas=6 -f replicaset-definition.yaml

## ReplicaSet의 replicas를 업데이트하는 방법

## Imperative 방식
# definition 파일의 replicas를 6으로 수정 후 반영
kubectl replace -f replicaset-definition.yaml
# kubectl scale 사용
kubectl scale &amp;mdash;replicas=6 -f replicaset-definition.yaml
kubectl scale &amp;mdash;replicas=6 replicaset myapp-replicaset

## Declarative 방식 (yaml 파일의 replicas를 6으로 수정)
kubectl apply -f replicaset-definition.yaml&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스터디/DevOps</category>
      <category>Kubernetes</category>
      <category>ReplicaSet</category>
      <author>훈스비</author>
      <guid isPermaLink="true">https://right1203.tistory.com/35</guid>
      <comments>https://right1203.tistory.com/35#entry35comment</comments>
      <pubDate>Sat, 29 Oct 2022 22:15:39 +0900</pubDate>
    </item>
  </channel>
</rss>