<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ai ML</title>
    <link>https://neneb-net.tistory.com/</link>
    <description>neneb-net 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 20 Jul 2026 12:18:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>aimler</managingEditor>
    <item>
      <title>jupyter notebook에서 dataframe 출력 옵션 변경</title>
      <link>https://neneb-net.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jupyter notebook에서 보통은 Dataframe을 화면에 출력할 때는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;display(dataframe.head()) 와 같은 코드를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 가끔은 내용을 복사하기 편하기 위해서 print(dataframe) 코드를 사용하는데요&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;794&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EbdbG/dJMcaf6jCNW/uvQxWnxPPVY9BM1q1ptKh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EbdbG/dJMcaf6jCNW/uvQxWnxPPVY9BM1q1ptKh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EbdbG/dJMcaf6jCNW/uvQxWnxPPVY9BM1q1ptKh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEbdbG%2FdJMcaf6jCNW%2FuvQxWnxPPVY9BM1q1ptKh0%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;794&quot; height=&quot;300&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때&lt;/p&gt;
&lt;pre id=&quot;code_1767160160141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pd.set_option('display.expand_frame_repr', False)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxHFNa/dJMcacPhRSK/F6CfCsJhh4vNwarx2m6L2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxHFNa/dJMcacPhRSK/F6CfCsJhh4vNwarx2m6L2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxHFNa/dJMcacPhRSK/F6CfCsJhh4vNwarx2m6L2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxHFNa%2FdJMcacPhRSK%2FF6CfCsJhh4vNwarx2m6L2K%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;865&quot; height=&quot;110&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;110&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>jupyter notebook</category>
      <category>print 줄바꿈</category>
      <category>Python</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/38</guid>
      <comments>https://neneb-net.tistory.com/38#entry38comment</comments>
      <pubDate>Wed, 31 Dec 2025 14:50:32 +0900</pubDate>
    </item>
    <item>
      <title>2025-12-05 트래커</title>
      <link>https://neneb-net.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;동작중인 89개 트래커 입니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; style=&quot;background-color: #ffffff;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;http://0123456789nonexistent.com:80/announce

http://0d.kebhana.mx:443/announce

http://107.189.2.131:1337/announce

http://1337.abcvg.info:80/announce

http://138.124.183.78:6969/announce

http://140.235.237.23:6969/announce

http://167.253.78.20:8080/announce

http://185.230.4.150:1337/announce

http://193.31.26.113:6969/announce

http://211.75.205.188:6969/announce

http://34.94.76.146:1337/announce

http://34.94.76.146:2701/announce

http://34.94.76.146:2710/announce

http://34.94.76.146:443/announce

http://34.94.76.146:6969/announce

http://34.94.76.146:80/announce

http://35.227.59.57:11450/announce

http://35.227.59.57:2710/announce

http://35.227.59.57:6969/announce

http://35.227.59.57:80/announce

http://35.227.59.57:80/announce.php

http://45.13.119.213:6969/announce

http://95.217.167.10:6969/announce

http://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce

http://bt.poletracker.org:2710/announce

http://bt1.xxxxbt.cc:6969/announce

http://buny.uk:6969/announce

http://extracker.dahrkael.net:6969/announce

http://home.yxgz.club:6969/announce

http://ipv4.rer.lol:2710/announce

http://open.trackerlist.xyz:80/announce

http://p4p.arenabg.com:1337/announce

http://retracker.spark-rostov.ru:80/announce

http://seeders-paradise.org:80/announce

http://servandroidkino.ru:80/announce

http://share.hkg-fansub.info:80/announce.php

http://shubt.net:2710/announce

http://t.overflow.biz:6969/announce

http://torrent.hificode.in:6969/announce

http://tracker.bittor.pw:1337/announce

http://tracker.bt-hash.com:80/announce

http://tracker.bt4g.com:2095/announce

http://tracker.bz:80/announce

http://tracker.corpscorp.online:80/announce

http://tracker.dmcomic.org:2710/announce

http://tracker.ghostchu-services.top:80/announce

http://tracker.ipv6tracker.org:80/announce

http://tracker.lintk.me:2710/announce

http://tracker.moxing.party:6969/announce

http://tracker.mywaifu.best:6969/announce

http://tracker.renfei.net:8080/announce

http://tracker.sbsub.com:2710/announce

http://tracker.tritan.gg:8080/announce

http://tracker.vanitycore.co:6969/announce

http://tracker.xiaoduola.xyz:6969/announce

http://tracker1.itzmx.com:8080/announce

http://tracker810.xyz:11450/announce

http://www.genesis-sp.org:2710/announce

http://www.torrentsnipe.info:2701/announce

https://tr.zukizuki.org:443/announce

https://tracker.bt4g.com:443/announce

https://tracker.ghostchu-services.top:443/announce

https://tracker.leechshield.link:443/announce

https://tracker.moeblog.cn:443/announce

https://tracker.pmman.tech:443/announce

https://tracker.yemekyedim.com:443/announce

udp://107.189.7.165:6969/announce

udp://111.90.151.241:6969/announce

udp://135.125.236.64:6969/announce

udp://176.99.7.59:6969/announce

udp://185.121.168.96:1337/announce

udp://185.243.218.213:80/announce

udp://193.42.111.57:9337/announce

udp://209.141.59.25:6969/announce

udp://211.75.205.187:80/announce

udp://211.75.205.188:6969/announce

udp://89.234.156.205:451/announce

udp://94.23.207.177:6969/announce

udp://bandito.byterunner.io:6969/announce

udp://d40969.acod.regrucolo.ru:6969/announce

udp://open.stealth.si:80/announce

udp://retracker.lanta.me:2710/announce

udp://retracker01-msk-virt.corbina.net:80/announce

udp://t.overflow.biz:6969/announce

udp://tr4ck3r.duckdns.org:6969/announce

udp://tracker.gmi.gd:6969/announce

udp://tracker.opentrackr.org:1337/announce

udp://tracker.qu.ax:6969/announce

udp://tracker2.dler.org:80/announce&lt;/code&gt;&lt;/pre&gt;</description>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/37</guid>
      <comments>https://neneb-net.tistory.com/37#entry37comment</comments>
      <pubDate>Fri, 5 Dec 2025 22:30:19 +0900</pubDate>
    </item>
    <item>
      <title>추천 오픈소스 메신저들</title>
      <link>https://neneb-net.tistory.com/36</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; Tinode&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;257&quot; data-start=&quot;165&quot;&gt;서버가 Go로 구현되어 있고, Android, iOS, Web 등 클라이언트를 지원합니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://github.com/tinode/chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;tinode.co&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;401&quot; data-start=&quot;258&quot;&gt;개인용부터 소규모 팀, 커뮤니티까지 유연하게 활용 가능하며, 그룹 채팅, 1:1 채팅, 채널, 파일/미디어 전송, 메시지 읽음/전달 상태, 마크다운 형식 등 기본 기능은 충실합니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://github.com/tinode/chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;502&quot; data-start=&quot;402&quot;&gt;서버를 직접 빌드하고 배포하는 것도 가능해서 &amp;ldquo;진정한 의미의 self-hosted 메신저&amp;rdquo;로 쓸 수 있어요.&lt;/li&gt;
&lt;li data-end=&quot;502&quot; data-start=&quot;402&quot;&gt;&lt;b&gt;장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;762&quot; data-start=&quot;513&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;601&quot; data-start=&quot;513&quot;&gt;완전 오픈소스 + self-host 가능. 코드 열람과 커스터마이즈가 자유롭습니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://tinode.co/?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;tinode.co&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;680&quot; data-start=&quot;602&quot;&gt;여러 플랫폼(Android, iOS, Web, CLI 등) 지원. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://github.com/tinode/chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;762&quot; data-start=&quot;681&quot;&gt;실시간 메시징 + 미디어/파일 공유 + 그룹/채널 기능 등 기본 충실. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://github.com/tinode/chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;778&quot; data-start=&quot;764&quot;&gt;&lt;b&gt;단점 / 주의점&lt;/b&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;778&quot; data-start=&quot;764&quot;&gt;&amp;ldquo;매우 가볍고 단순한 채팅앱&amp;rdquo;이라기보다는, 꽤 기능이 많아 서버 셋업 + 유지보수는 개발 지식 필요.&lt;/li&gt;
&lt;li data-end=&quot;778&quot; data-start=&quot;764&quot;&gt;&amp;ldquo;메신저 + 업무 협업툴&amp;rdquo; 급이 아니라서, 채널 권한 제어나 고급 협업 기능은 제한적.&lt;/li&gt;
&lt;li data-end=&quot;778&quot; data-start=&quot;764&quot;&gt;처음 설정 시 문서 보고 구성하는 데 약간의 진입장벽 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; Rocket.Chat&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-end=&quot;1180&quot; data-start=&quot;957&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;1083&quot; data-start=&quot;957&quot;&gt;오픈소스 팀 커뮤니케이션 플랫폼으로, 실시간 메시지, 그룹/채널, 파일 공유, 음성/화상 통화, 스크린 공유 등 비교적 풍부한 기능을 갖추고 있습니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Rocket.Chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;Slant&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;Node.js + MongoDB 기반이며, Docker 등을 통해 self-host가 가능합니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Rocket.Chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;&lt;b&gt;장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;협업 기능이 많아 &amp;ldquo;단순 채팅&amp;rdquo;을 넘어 업무용 또는 팀용 메신저로도 활용 가능.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://en.wikipedia.org/wiki/Rocket.Chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;rocket.chat&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;오픈소스 + self-host + 커스터마이즈 자유.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://en.wikipedia.org/wiki/Rocket.Chat?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;커뮤니티와 개발자 지원 비교적 활발. 다양한 플러그인이나 연동도 가능.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot; data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://www.slant.co/versus/10600/12763/~rocket-chat_vs_mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Slant&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;&lt;b&gt;단점 / 주의점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;서버 리소스 소모가 꽤 있어서, 개인용 / 작은 서버에선 과할 수 있어요. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://best-of-web.builder.io/library/mattermost/mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Builder.io&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;설정과 유지보수가 복잡할 수 있고, 관리가 좀 번거로울 수 있음. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://best-of-web.builder.io/library/mattermost/mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Builder.io&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1084&quot;&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; &amp;ldquo;가볍고 단순한 채팅&amp;rdquo;보다는 &amp;ldquo;팀 협업용&amp;rdquo;에 가까움 &amp;mdash; 필요 이상의 기능이 많아 오히려 부담일 수 있음. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; Mattermost&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1807&quot; data-start=&quot;1707&quot;&gt;&amp;ldquo;자체 서버에 설치 가능한 슬랙(Slack) 대안&amp;rdquo;으로 많이 거론되는 오픈소스 메신저/협업 툴입니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;Mattermost.com&lt;/span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;팀 메시징, 파일/미디어 공유, 다양한 플러그인/통합, 채널 기반 커뮤니케이션 등 기능을 갖추고 있어, 개발팀이나 소규모 회사 내부용으로 많이 사용됩니다. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://best-of-web.builder.io/library/mattermost/mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Builder.io&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&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 data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;Slack과 유사한 경험: 채널, DM, 파일공유, 알림 등 기본 충실. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;오픈소스 + self-host 가능 + 필요한 경우 확장/삽질도 가능. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;위키피디아&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;개발 환경 / DevOps / 협업용 워크플로우 자동화 등과 잘 어울리는 도구. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://mattermost.com/mattermost-vs-rocketchat/?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Mattermost.com&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;&amp;nbsp;&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 data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;&amp;ldquo;경량 메신저&amp;rdquo;라기엔 기능이 많고, 설정 / 유지가 꽤 복잡함.&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;음성/화상 통화 같은 기능은 기본이 아니어서, 이런 기능 필요하면 별도 설치 필요. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://www.rocket.chat/blog/slack-alternative?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;rocket.chat&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1938&quot; data-start=&quot;1808&quot;&gt;오픈소스라고는 하지만, 일부 고급 기능이나 확장에는 제약이나 유료 요소가 있을 수 있어요. &lt;span data-state=&quot;closed&quot;&gt;&lt;span data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a href=&quot;https://best-of-web.builder.io/library/mattermost/mattermost?utm_source=chatgpt.com&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Builder.io&lt;/span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>메신저</category>
      <category>오픈소스 메신저</category>
      <category>추천</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/36</guid>
      <comments>https://neneb-net.tistory.com/36#entry36comment</comments>
      <pubDate>Thu, 27 Nov 2025 21:45:27 +0900</pubDate>
    </item>
    <item>
      <title>ClickHouse 와 DuckDB / MySQL 과 비교</title>
      <link>https://neneb-net.tistory.com/35</link>
      <description>&lt;h1 data-end=&quot;178&quot; data-start=&quot;153&quot;&gt;  1. ClickHouse란 무엇인가?&lt;/h1&gt;
&lt;p data-end=&quot;253&quot; data-start=&quot;180&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ClickHouse&lt;/b&gt;는 Yandex가 개발한 &lt;b&gt;컬럼 지향(Columnar) OLAP 데이터베이스&lt;/b&gt;입니다.&lt;br /&gt;핵심 컨셉은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;340&quot; data-start=&quot;255&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;283&quot; data-start=&quot;255&quot;&gt;&lt;b&gt;초고속 읽기 성능&lt;/b&gt; (OLAP 분석 쿼리)&lt;/li&gt;
&lt;li data-end=&quot;296&quot; data-start=&quot;284&quot;&gt;&lt;b&gt;압축률 우수&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;311&quot; data-start=&quot;297&quot;&gt;&lt;b&gt;분산 처리 가능&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;340&quot; data-start=&quot;312&quot;&gt;&lt;b&gt;실시간 분석(near real-time)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;402&quot; data-start=&quot;342&quot; data-ke-size=&quot;size16&quot;&gt;ClickHouse는 &amp;ldquo;데이터 웨어하우스 + 실시간 로그&amp;middot;이벤트 분석 엔진&amp;rdquo;의 중간 형태로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;412&quot; data-start=&quot;404&quot; data-ke-size=&quot;size16&quot;&gt;대표적 사용처:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;516&quot; data-start=&quot;413&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;429&quot; data-start=&quot;413&quot;&gt;로그 분석 (ELK 대체)&lt;/li&gt;
&lt;li data-end=&quot;465&quot; data-start=&quot;430&quot;&gt;모니터링/Metric 저장 (Prometheus 대체 가능)&lt;/li&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;466&quot;&gt;이벤트 분석 (사용자 행동 로그)&lt;/li&gt;
&lt;li data-end=&quot;503&quot; data-start=&quot;487&quot;&gt;대규모 시계열 데이터 처리&lt;/li&gt;
&lt;li data-end=&quot;516&quot; data-start=&quot;504&quot;&gt;BI/OLAP 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;   2. ClickHouse vs MySQL 비교 &lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 244px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; 구분 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; ClickHouse &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; MySQL &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;DB 유형&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;OLAP (컬럼 지향)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;OLTP (행(Row) 지향)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;대규모 읽기/집계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실시간 트랜잭션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;쓰기 성능&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;매우 빠름(배치 인서트 최적)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;빠름(단건 트랜잭션 강함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;읽기 성능&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;대량 데이터 집계에 매우 강함&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;단건 조회/조인에 강함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;인덱스&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;스킵 인덱스, 파티션, sparse index&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;B-Tree 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;조인&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;제한적, 새로운 버전에서 개선됨&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;매우 강력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;트랜잭션&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;거의 없음 (OLAP이므로)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;ACID 준수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;확장성&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;수평 확장 매우 우수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;수평 확장 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;저장 구조&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;컬럼 저장 + 고압축&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;행 기반 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;활용 적합성&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;로그, 이벤트 분석, 대용량 OLAP&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;주문/결제 시스템, ERP, CRUD 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1062&quot; data-start=&quot;1052&quot; data-ke-size=&quot;size23&quot;&gt;요약하면&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1127&quot; data-start=&quot;1063&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1091&quot; data-start=&quot;1063&quot;&gt;&lt;b&gt;MySQL &amp;rarr; 서비스 DB(트랜잭션)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1127&quot; data-start=&quot;1092&quot;&gt;&lt;b&gt;ClickHouse &amp;rarr; 분석, 로그, 대규모 집계&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1195&quot; data-start=&quot;1129&quot; data-ke-size=&quot;size16&quot;&gt;특히 &lt;b&gt;초당 수천만 로그를 넣고 바로 분석하는 구조에서는 ClickHouse가 MySQL을 압도적으로 능가&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-end=&quot;1195&quot; data-start=&quot;1129&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;1234&quot; data-start=&quot;1202&quot;&gt;  3. ClickHouse vs DuckDB 차이점&lt;/h1&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1236&quot; data-ke-size=&quot;size16&quot;&gt;DuckDB와 ClickHouse는 둘 다 &lt;b&gt;OLAP 엔진&lt;/b&gt;입니다. 그러나 철학이 완전히 다릅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;ClickHouse&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; DuckDB &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설계 철학&lt;/td&gt;
&lt;td&gt;서버형 OLAP DB, 분산 확장&lt;/td&gt;
&lt;td&gt;&lt;b&gt;임베디드 OLAP 엔진&lt;/b&gt;, 파일 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설치 형태&lt;/td&gt;
&lt;td&gt;서버/클러스터로 동작&lt;/td&gt;
&lt;td&gt;Python&amp;middot;App에 포함되는 DB (SQLite처럼)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 규모&lt;/td&gt;
&lt;td&gt;수십~수백억 데이터&lt;/td&gt;
&lt;td&gt;싱글 머신 메모리 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스토리지&lt;/td&gt;
&lt;td&gt;서버 스토리지, S3 등&lt;/td&gt;
&lt;td&gt;Parquet/CSV/Local 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 목적&lt;/td&gt;
&lt;td&gt;로그 플랫폼, DW, 실시간 분석&lt;/td&gt;
&lt;td&gt;로컬 데이터 분석, 파이프라인, 임시 쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;확장성&lt;/td&gt;
&lt;td&gt;&lt;b&gt;수평 확장 가능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;단일 노드 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커넥션 방식&lt;/td&gt;
&lt;td&gt;TCP/HTTP 서버&lt;/td&gt;
&lt;td&gt;라이브러리 호출(duckdb.connect())&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;쓰기 방식&lt;/td&gt;
&lt;td&gt;대량 배치 삽입 최적화&lt;/td&gt;
&lt;td&gt;파일 스캔 기반 즉시 분석에 강함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운영 비용&lt;/td&gt;
&lt;td&gt;운영 필요&lt;/td&gt;
&lt;td&gt;운영 비용 거의 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;쿼리 성능&lt;/td&gt;
&lt;td&gt;대규모 데이터 집계 우수&lt;/td&gt;
&lt;td&gt;소규모~중규모 데이터 분석 최강&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1236&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1859&quot; data-start=&quot;1844&quot; data-ke-size=&quot;size23&quot;&gt;가장 중요한 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1947&quot; data-start=&quot;1860&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1896&quot; data-start=&quot;1860&quot;&gt;&lt;b&gt;ClickHouse = 서버 기반 고성능 분석 DB&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1947&quot; data-start=&quot;1897&quot;&gt;&lt;b&gt;DuckDB = 애플리케이션 내부 분석 엔진 (SQLite의 OLAP 버전)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2018&quot; data-start=&quot;1949&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 OLAP이지만&lt;br /&gt;&lt;b&gt;ClickHouse는 운영형(operational), DuckDB는 임베디드 분석형&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-end=&quot;2018&quot; data-start=&quot;1949&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;2054&quot; data-start=&quot;2025&quot;&gt;  4. 어떤 상황에 어떤 DB를 써야 하는가?&lt;/h1&gt;
&lt;h3 data-end=&quot;2079&quot; data-start=&quot;2056&quot; data-ke-size=&quot;size23&quot;&gt;  ClickHouse 추천 상황&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2186&quot; data-start=&quot;2080&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2100&quot; data-start=&quot;2080&quot;&gt;초당 수십만 ~ 수천만 로그 적재&lt;/li&gt;
&lt;li data-end=&quot;2122&quot; data-start=&quot;2101&quot;&gt;엔터프라이즈 수준의 실시간 대시보드&lt;/li&gt;
&lt;li data-end=&quot;2143&quot; data-start=&quot;2123&quot;&gt;Grafana, Kibana 대체&lt;/li&gt;
&lt;li data-end=&quot;2175&quot; data-start=&quot;2144&quot;&gt;이벤트 분석(Amplitude/Mixpanel 대체)&lt;/li&gt;
&lt;li data-end=&quot;2186&quot; data-start=&quot;2176&quot;&gt;분산 환경 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2207&quot; data-start=&quot;2188&quot; data-ke-size=&quot;size23&quot;&gt;  DuckDB 추천 상황&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2298&quot; data-start=&quot;2208&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2226&quot; data-start=&quot;2208&quot;&gt;Python 기반 데이터 분석&lt;/li&gt;
&lt;li data-end=&quot;2246&quot; data-start=&quot;2227&quot;&gt;CSV/Parquet 파일 분석&lt;/li&gt;
&lt;li data-end=&quot;2273&quot; data-start=&quot;2247&quot;&gt;로그 파이프라인의 임시 ETL, 일시적 분석&lt;/li&gt;
&lt;li data-end=&quot;2298&quot; data-start=&quot;2274&quot;&gt;서버 없이 로컬/단일 노드에서 빠른 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2318&quot; data-start=&quot;2300&quot; data-ke-size=&quot;size23&quot;&gt;  MySQL 추천 상황&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2383&quot; data-start=&quot;2319&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2335&quot; data-start=&quot;2319&quot;&gt;CRUD 중심 서비스 DB&lt;/li&gt;
&lt;li data-end=&quot;2357&quot; data-start=&quot;2336&quot;&gt;사용자/상품/결제 등 트랜잭션 중심&lt;/li&gt;
&lt;li data-end=&quot;2383&quot; data-start=&quot;2358&quot;&gt;OLAP에는 부적합 (복잡한 집계가 느림)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;   5. 로그 분석을 기준으로 한 선택 가이드 &lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;로그 규모&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;추천&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수십 GB 이하&lt;/td&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수백 GB~수 TB (싱글 서버 가능)&lt;/td&gt;
&lt;td&gt;ClickHouse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수 TB 이상 + 분산 필요&lt;/td&gt;
&lt;td&gt;ClickHouse 클러스터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;트랜잭션 로그 저장/일반 서비스 로그&lt;/td&gt;
&lt;td&gt;MySQL(OLTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;임시 분석/개발환경에서 분석&lt;/td&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;2639&quot; data-start=&quot;2629&quot;&gt;  최종 요약&lt;/h1&gt;
&lt;h3 data-end=&quot;2655&quot; data-start=&quot;2641&quot; data-ke-size=&quot;size23&quot;&gt;ClickHouse&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2730&quot; data-start=&quot;2656&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2679&quot; data-start=&quot;2656&quot;&gt;&lt;b&gt;고성능 컬럼 기반 OLAP DB&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2700&quot; data-start=&quot;2680&quot;&gt;로그/이벤트/모니터링 분석에 최적&lt;/li&gt;
&lt;li data-end=&quot;2730&quot; data-start=&quot;2701&quot;&gt;&lt;b&gt;MySQL보다 훨씬 빠른 대규모 집계 성능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2741&quot; data-start=&quot;2732&quot; data-ke-size=&quot;size23&quot;&gt;MySQL&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2797&quot; data-start=&quot;2742&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2758&quot; data-start=&quot;2742&quot;&gt;&lt;b&gt;트랜잭션(DBMS)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2775&quot; data-start=&quot;2759&quot;&gt;CRUD&amp;middot;조인 처리에 강함&lt;/li&gt;
&lt;li data-end=&quot;2797&quot; data-start=&quot;2776&quot;&gt;OLAP&amp;middot;로그 분석은 적합하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2809&quot; data-start=&quot;2799&quot; data-ke-size=&quot;size23&quot;&gt;DuckDB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2884&quot; data-start=&quot;2810&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2831&quot; data-start=&quot;2810&quot;&gt;&lt;b&gt;로컬/임베디드 OLAP 엔진&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2853&quot; data-start=&quot;2832&quot;&gt;파일 분석/ETL/개발환경에서 최강&lt;/li&gt;
&lt;li data-end=&quot;2884&quot; data-start=&quot;2854&quot;&gt;ClickHouse처럼 서버&amp;middot;클러스터 운영은 불가능&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>clickhouse</category>
      <category>DuckDB</category>
      <category>mysql</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/35</guid>
      <comments>https://neneb-net.tistory.com/35#entry35comment</comments>
      <pubDate>Fri, 14 Nov 2025 18:37:34 +0900</pubDate>
    </item>
    <item>
      <title>시계열 데이터의 비정상성을 제거하기 위해 사용하는 잔차를 편차로 대체할 수 있는지 여</title>
      <link>https://neneb-net.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;결론: 시계열에서 비정상성을 제거하려는 목적이라면 &amp;lsquo;잔차(residual)&amp;rsquo; 대신 &amp;lsquo;편차(deviation)&amp;rsquo;를 사용하면 안됩니다.&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;h1 data-end=&quot;232&quot; data-start=&quot;205&quot;&gt;1) &amp;ldquo;편차(deviation)&amp;rdquo;는 무엇인가?&lt;/h1&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;233&quot; data-ke-size=&quot;size16&quot;&gt;편차 = 각 값에서 &amp;ldquo;전체 평균&amp;rdquo;을 뺀 값&lt;/p&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;233&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;296&quot; data-start=&quot;286&quot; data-ke-size=&quot;size16&quot;&gt;문제는 이것입니다:&lt;/p&gt;
&lt;h3 data-end=&quot;343&quot; data-start=&quot;298&quot; data-ke-size=&quot;size23&quot;&gt;✔ 편차는 **평균이 일정한 데이터(정상성)**라는 전제를 필요로 한다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;455&quot; data-start=&quot;344&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;390&quot; data-start=&quot;344&quot;&gt;비정상 시계열은 보통 &lt;b&gt;시간에 따라 평균이 변합니다(트렌드, 계절성)&lt;/b&gt;.&lt;/li&gt;
&lt;li data-end=&quot;455&quot; data-start=&quot;391&quot;&gt;그런데 편차는 단지 &amp;ldquo;전체 평균&amp;rdquo; 한 번 빼줄 뿐이기 때문에, 시간에 따라 바뀌는 패턴을 제거해 주지 못합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;459&quot; data-start=&quot;457&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-end=&quot;525&quot; data-start=&quot;460&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;525&quot; data-start=&quot;462&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;편차는 트렌드도, 계절성도 제거하지 못합니다. 시계열이 비정상이라면 편차를 써도 비정상은 그대로 남습니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;233&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;558&quot; data-start=&quot;532&quot;&gt;2) &amp;ldquo;잔차(residual)&amp;rdquo;는 무엇인가?&lt;/h1&gt;
&lt;p data-end=&quot;590&quot; data-start=&quot;559&quot; data-ke-size=&quot;size16&quot;&gt;잔차 = 모델이 설명한 부분을 제거한 후 남은 값&lt;/p&gt;
&lt;p data-end=&quot;590&quot; data-start=&quot;559&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;902&quot; data-start=&quot;855&quot; data-ke-size=&quot;size23&quot;&gt;✔ 잔차는 &lt;b&gt;데이터에서 비정상성(트렌드&amp;middot;계절성)을 제거한 상태&lt;/b&gt;가 된다&lt;/h3&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;903&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-end=&quot;957&quot; data-start=&quot;906&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;957&quot; data-start=&quot;908&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잔차는 평균이 일정하고 분산이 비교적 안정된 정상 시계열을 얻기 위한 방법입니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;1421&quot; data-start=&quot;1403&quot;&gt;3) 편차는 언제 사용하는가?&lt;/h1&gt;
&lt;p data-end=&quot;1493&quot; data-start=&quot;1422&quot; data-ke-size=&quot;size16&quot;&gt;편차(평균 중심화)는 &lt;b&gt;회귀나 PCA 같은 일반 머신러닝 전처리&lt;/b&gt;에서 쓰는 개념이지, 시계열 비정상성 제거 용도는 아닙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1545&quot; data-start=&quot;1495&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1523&quot; data-start=&quot;1495&quot;&gt;단순히 스케일링을 위해 사용하는 것일 뿐&lt;/li&gt;
&lt;li data-end=&quot;1545&quot; data-start=&quot;1524&quot;&gt;시간에 따른 패턴 제거 기능은 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 data-end=&quot;1568&quot; data-start=&quot;1552&quot;&gt;4) 잔차를 쓰면 좋은 점&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1704&quot; data-start=&quot;1569&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1608&quot; data-start=&quot;1569&quot;&gt;시계열 분해(STL, X11 등) &amp;rarr; 잔차 = 비정상 요소 제거&lt;/li&gt;
&lt;li data-end=&quot;1643&quot; data-start=&quot;1609&quot;&gt;ARIMA 학습 시 필수인 &amp;ldquo;정상성&amp;rdquo;을 만족시키기 쉬움&lt;/li&gt;
&lt;li data-end=&quot;1670&quot; data-start=&quot;1644&quot;&gt;외생변수 영향 제거(회귀 후 잔차 사용)&lt;/li&gt;
&lt;li data-end=&quot;1704&quot; data-start=&quot;1671&quot;&gt;계절성 제거 필수 분석(매출, 트래픽 등에서 매우 중요)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 data-end=&quot;1721&quot; data-start=&quot;1711&quot;&gt;5) 핵심 결론&lt;/h1&gt;
&lt;blockquote data-end=&quot;1813&quot; data-start=&quot;1722&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1813&quot; data-start=&quot;1724&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;편차는 전체 평균만 제거하므로 비정상성 제거에 효과가 없다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;잔차는 트렌드&amp;middot;계절성&amp;middot;구조적 패턴을 제거하여 정상성에 근접한 데이터를 만든다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1868&quot; data-start=&quot;1815&quot; data-ke-size=&quot;size16&quot;&gt;그래서 시계열의 정상화를 목표로 한다면 반드시 **잔차(residual)**를 사용해야 합니다.&lt;/p&gt;</description>
      <category>시계열데이터</category>
      <category>잔차</category>
      <category>편차</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/33</guid>
      <comments>https://neneb-net.tistory.com/33#entry33comment</comments>
      <pubDate>Fri, 14 Nov 2025 18:21:02 +0900</pubDate>
    </item>
    <item>
      <title>Fusion 나사산 별로 출력한 결과물 사진입니다.</title>
      <link>https://neneb-net.tistory.com/32</link>
      <description>&lt;p data-end=&quot;82&quot; data-start=&quot;36&quot; data-ke-size=&quot;size16&quot;&gt;3D 프린터로 출력할 제품을 설계하다 보면 나사산을 사용할 일이 자주 생깁니다.&lt;/p&gt;
&lt;p data-end=&quot;175&quot; data-start=&quot;84&quot; data-ke-size=&quot;size16&quot;&gt;그동안은 가장 일반적인 &lt;b&gt;ISO 미터법 나사산 프로파일&lt;/b&gt;을 주로 사용해 왔지만,&lt;br /&gt;상황에 따라 좀 더 &lt;b&gt;폭이 넓은 나사산&lt;/b&gt;이 필요한 경우도 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;226&quot; data-start=&quot;177&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이번 기회에 &lt;b&gt;여러 종류의 나사산을 직접 출력해 비교&lt;/b&gt;해 보기로 했습니다.&lt;/p&gt;
&lt;p data-end=&quot;226&quot; data-start=&quot;177&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Fusion의 나사산 목록입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-11-09_Fusion_나사산_목록.png&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;881&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BRJC1/dJMcaest2XZ/G0EKbpxbDjLEMBazHFZfkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BRJC1/dJMcaest2XZ/G0EKbpxbDjLEMBazHFZfkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BRJC1/dJMcaest2XZ/G0EKbpxbDjLEMBazHFZfkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBRJC1%2FdJMcaest2XZ%2FG0EKbpxbDjLEMBazHFZfkK%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;463&quot; height=&quot;881&quot; data-filename=&quot;2025-11-09_Fusion_나사산_목록.png&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;881&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Bamublab A1 mini를 사용한 출력물 사진입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;DSC04353_title.JPG&quot; data-origin-width=&quot;6093&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMlPcT/dJMcagjw5OV/Ep15H5EmfixHAnqdEkMZWK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMlPcT/dJMcagjw5OV/Ep15H5EmfixHAnqdEkMZWK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMlPcT/dJMcagjw5OV/Ep15H5EmfixHAnqdEkMZWK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMlPcT%2FdJMcagjw5OV%2FEp15H5EmfixHAnqdEkMZWK%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;6093&quot; height=&quot;4000&quot; data-filename=&quot;DSC04353_title.JPG&quot; data-origin-width=&quot;6093&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출력물 Line별로 확대한 이미지입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_5.JPG&quot; data-origin-width=&quot;4763&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmOpK2/dJMcakfahgo/Hmg9KYkZEeluaVKkAkymek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmOpK2/dJMcakfahgo/Hmg9KYkZEeluaVKkAkymek/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmOpK2/dJMcakfahgo/Hmg9KYkZEeluaVKkAkymek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmOpK2%2FdJMcakfahgo%2FHmg9KYkZEeluaVKkAkymek%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;4763&quot; height=&quot;870&quot; data-filename=&quot;1_5.JPG&quot; data-origin-width=&quot;4763&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6_9.JPG&quot; data-origin-width=&quot;5094&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6ccKE/dJMcahQhiJB/q0HcOHSmgh0ucnDv4RqPa0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6ccKE/dJMcahQhiJB/q0HcOHSmgh0ucnDv4RqPa0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6ccKE/dJMcahQhiJB/q0HcOHSmgh0ucnDv4RqPa0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6ccKE%2FdJMcahQhiJB%2Fq0HcOHSmgh0ucnDv4RqPa0%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;5094&quot; height=&quot;744&quot; data-filename=&quot;6_9.JPG&quot; data-origin-width=&quot;5094&quot; data-origin-height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10_13.JPG&quot; data-origin-width=&quot;5350&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjEpiH/dJMcae65xQR/08LGTcGaDA45RGs7qV4zB0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjEpiH/dJMcae65xQR/08LGTcGaDA45RGs7qV4zB0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjEpiH/dJMcae65xQR/08LGTcGaDA45RGs7qV4zB0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjEpiH%2FdJMcae65xQR%2F08LGTcGaDA45RGs7qV4zB0%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;5350&quot; height=&quot;802&quot; data-filename=&quot;10_13.JPG&quot; data-origin-width=&quot;5350&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14_17.JPG&quot; data-origin-width=&quot;5681&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ygUgj/dJMcae65xQS/IdBbGlyE6kg4jv07CpDBg1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ygUgj/dJMcae65xQS/IdBbGlyE6kg4jv07CpDBg1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ygUgj/dJMcae65xQS/IdBbGlyE6kg4jv07CpDBg1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FygUgj%2FdJMcae65xQS%2FIdBbGlyE6kg4jv07CpDBg1%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;5681&quot; height=&quot;1026&quot; data-filename=&quot;14_17.JPG&quot; data-origin-width=&quot;5681&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>3d 프린터 나사</category>
      <category>bambulab 나사</category>
      <category>fusion 나사산</category>
      <category>나사</category>
      <category>볼트</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/32</guid>
      <comments>https://neneb-net.tistory.com/32#entry32comment</comments>
      <pubDate>Sun, 9 Nov 2025 22:16:48 +0900</pubDate>
    </item>
    <item>
      <title>사내에서 Langchain 사용시 발생하는 SSL 오류 해결방법</title>
      <link>https://neneb-net.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사내에서 Langchain을 사용할 경우 아래와 같이 오류가 발생한다면 모듈 파일을 수정해서 오류를 우회할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;✅&lt;/span&gt; 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761628237549&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;llm = ChatOpenAI(
    api_key=&quot;USER API Key&quot;,
    model=&quot;gpt-4o-mini&quot; 
)

@tool
def get_weather(city: str) -&amp;gt; str:
    &quot;&quot;&quot;Get weather for a given city.&quot;&quot;&quot; 
    return f&quot;It's always sunny in {city}!&quot; 

agent = create_agent(
    model=llm,
    tools=[get_weather],
    system_prompt=&quot;You are a helpful assistant&quot;,
)

# Run the agent
agent.invoke(
    {&quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;what is the weather in sf&quot;}]}
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;✅ 오류 내용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761628322447&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---------------------------------------------------------------------------
ConnectError                              Traceback (most recent call last)
File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_transports\default.py:101, in map_httpcore_exceptions()
    100 try:
--&amp;gt; 101     yield
    102 except Exception as exc:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_transports\default.py:250, in HTTPTransport.handle_request(self, request)
    249 with map_httpcore_exceptions():
--&amp;gt; 250     resp = self._pool.handle_request(req)
    252 assert isinstance(resp.stream, typing.Iterable)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_sync\connection_pool.py:256, in ConnectionPool.handle_request(self, request)
    255     self._close_connections(closing)
--&amp;gt; 256     raise exc from None
    258 # Return the response. Note that in this case we still have to manage
    259 # the point at which the response is closed.

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_sync\connection_pool.py:236, in ConnectionPool.handle_request(self, request)
    234 try:
    235     # Send the request on the assigned connection.
--&amp;gt; 236     response = connection.handle_request(
    237         pool_request.request
    238     )
    239 except ConnectionNotAvailable:
    240     # In some cases a connection may initially be available to
    241     # handle a request, but then become unavailable.
    242     #
    243     # In this case we clear the connection and try again.

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_sync\connection.py:101, in HTTPConnection.handle_request(self, request)
    100     self._connect_failed = True
--&amp;gt; 101     raise exc
    103 return self._connection.handle_request(request)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_sync\connection.py:78, in HTTPConnection.handle_request(self, request)
     77 if self._connection is None:
---&amp;gt; 78     stream = self._connect(request)
     80     ssl_object = stream.get_extra_info(&quot;ssl_object&quot;)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_sync\connection.py:156, in HTTPConnection._connect(self, request)
    155 with Trace(&quot;start_tls&quot;, logger, request, kwargs) as trace:
--&amp;gt; 156     stream = stream.start_tls(**kwargs)
    157     trace.return_value = stream

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_backends\sync.py:154, in SyncStream.start_tls(self, ssl_context, server_hostname, timeout)
    150 exc_map: ExceptionMapping = {
    151     socket.timeout: ConnectTimeout,
    152     OSError: ConnectError,
    153 }
--&amp;gt; 154 with map_exceptions(exc_map):
    155     try:

File c:\Users\User\.conda\envs\Orange\Lib\contextlib.py:158, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    157 try:
--&amp;gt; 158     self.gen.throw(value)
    159 except StopIteration as exc:
    160     # Suppress StopIteration *unless* it's the same exception that
    161     # was passed to throw().  This prevents a StopIteration
    162     # raised inside the &quot;with&quot; statement from being suppressed.

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpcore\_exceptions.py:14, in map_exceptions(map)
     13     if isinstance(exc, from_exc):
---&amp;gt; 14         raise to_exc(exc) from exc
     15 raise

ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)

The above exception was the direct cause of the following exception:

ConnectError                              Traceback (most recent call last)
File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\_base_client.py:982, in SyncAPIClient.request(self, cast_to, options, stream, stream_cls)
    981 try:
--&amp;gt; 982     response = self._client.send(
    983         request,
    984         stream=stream or self._should_stream_response_body(request=request),
    985         **kwargs,
    986     )
    987 except httpx.TimeoutException as err:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_client.py:914, in Client.send(self, request, stream, auth, follow_redirects)
    912 auth = self._build_request_auth(request, auth)
--&amp;gt; 914 response = self._send_handling_auth(
    915     request,
    916     auth=auth,
    917     follow_redirects=follow_redirects,
    918     history=[],
    919 )
    920 try:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_client.py:942, in Client._send_handling_auth(self, request, auth, follow_redirects, history)
    941 while True:
--&amp;gt; 942     response = self._send_handling_redirects(
    943         request,
    944         follow_redirects=follow_redirects,
    945         history=history,
    946     )
    947     try:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_client.py:979, in Client._send_handling_redirects(self, request, follow_redirects, history)
    977     hook(request)
--&amp;gt; 979 response = self._send_single_request(request)
    980 try:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_client.py:1014, in Client._send_single_request(self, request)
   1013 with request_context(request=request):
-&amp;gt; 1014     response = transport.handle_request(request)
   1016 assert isinstance(response.stream, SyncByteStream)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_transports\default.py:249, in HTTPTransport.handle_request(self, request)
    237 req = httpcore.Request(
    238     method=request.method,
    239     url=httpcore.URL(
   (...)    247     extensions=request.extensions,
    248 )
--&amp;gt; 249 with map_httpcore_exceptions():
    250     resp = self._pool.handle_request(req)

File c:\Users\User\.conda\envs\Orange\Lib\contextlib.py:158, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    157 try:
--&amp;gt; 158     self.gen.throw(value)
    159 except StopIteration as exc:
    160     # Suppress StopIteration *unless* it's the same exception that
    161     # was passed to throw().  This prevents a StopIteration
    162     # raised inside the &quot;with&quot; statement from being suppressed.

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_transports\default.py:118, in map_httpcore_exceptions()
    117 message = str(exc)
--&amp;gt; 118 raise mapped_exc(message) from exc

ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)

The above exception was the direct cause of the following exception:

APIConnectionError                        Traceback (most recent call last)
Cell In[3], line 17
     10 agent = create_agent(
     11     model=llm,
     12     tools=[get_weather],
     13     system_prompt=&quot;You are a helpful assistant&quot;,
     14 )
     16 # Run the agent
---&amp;gt; 17 agent.invoke(
     18     {&quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;what is the weather in sf&quot;}]}
     19 )

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\pregel\main.py:3094, in Pregel.invoke(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, **kwargs)
   3091 chunks: list[dict[str, Any] | Any] = []
   3092 interrupts: list[Interrupt] = []
-&amp;gt; 3094 for chunk in self.stream(
   3095     input,
   3096     config,
   3097     context=context,
   3098     stream_mode=[&quot;updates&quot;, &quot;values&quot;]
   3099     if stream_mode == &quot;values&quot; 
   3100     else stream_mode,
   3101     print_mode=print_mode,
   3102     output_keys=output_keys,
   3103     interrupt_before=interrupt_before,
   3104     interrupt_after=interrupt_after,
   3105     durability=durability,
   3106     **kwargs,
   3107 ):
   3108     if stream_mode == &quot;values&quot;:
   3109         if len(chunk) == 2:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\pregel\main.py:2679, in Pregel.stream(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, subgraphs, debug, **kwargs)
   2677 for task in loop.match_cached_writes():
   2678     loop.output_writes(task.id, task.writes, cached=True)
-&amp;gt; 2679 for _ in runner.tick(
   2680     [t for t in loop.tasks.values() if not t.writes],
   2681     timeout=self.step_timeout,
   2682     get_waiter=get_waiter,
   2683     schedule_task=loop.accept_push,
   2684 ):
   2685     # emit output
   2686     yield from _output(
   2687         stream_mode, print_mode, subgraphs, stream.get, queue.Empty
   2688     )
   2689 loop.after_tick()

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\pregel\_runner.py:167, in PregelRunner.tick(self, tasks, reraise, timeout, retry_policy, get_waiter, schedule_task)
    165 t = tasks[0]
    166 try:
--&amp;gt; 167     run_with_retry(
    168         t,
    169         retry_policy,
    170         configurable={
    171             CONFIG_KEY_CALL: partial(
    172                 _call,
    173                 weakref.ref(t),
    174                 retry_policy=retry_policy,
    175                 futures=weakref.ref(futures),
    176                 schedule_task=schedule_task,
    177                 submit=self.submit,
    178             ),
    179         },
    180     )
    181     self.commit(t, None)
    182 except Exception as exc:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\pregel\_retry.py:42, in run_with_retry(task, retry_policy, configurable)
     40     task.writes.clear()
     41     # run the task
---&amp;gt; 42     return task.proc.invoke(task.input, config)
     43 except ParentCommand as exc:
     44     ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\_internal\_runnable.py:656, in RunnableSeq.invoke(self, input, config, **kwargs)
    654     # run in context
    655     with set_config_context(config, run) as context:
--&amp;gt; 656         input = context.run(step.invoke, input, config, **kwargs)
    657 else:
    658     input = step.invoke(input, config)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langgraph\_internal\_runnable.py:400, in RunnableCallable.invoke(self, input, config, **kwargs)
    398         run_manager.on_chain_end(ret)
    399 else:
--&amp;gt; 400     ret = self.func(*args, **kwargs)
    401 if self.recurse and isinstance(ret, Runnable):
    402     return ret.invoke(input, config)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain\agents\factory.py:1034, in create_agent.&amp;lt;locals&amp;gt;.model_node(state, runtime)
   1021 request = ModelRequest(
   1022     model=model,
   1023     tools=default_tools,
   (...)   1029     runtime=runtime,
   1030 )
   1032 if wrap_model_call_handler is None:
   1033     # No handlers - execute directly
-&amp;gt; 1034     response = _execute_model_sync(request)
   1035 else:
   1036     # Call composed handler with base handler
   1037     response = wrap_model_call_handler(request, _execute_model_sync)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain\agents\factory.py:1007, in create_agent.&amp;lt;locals&amp;gt;._execute_model_sync(request)
   1004 if request.system_prompt:
   1005     messages = [SystemMessage(request.system_prompt), *messages]
-&amp;gt; 1007 output = model_.invoke(messages)
   1009 # Handle model output to get messages and structured_response
   1010 handled_output = _handle_model_output(output, effective_response_format)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_core\runnables\base.py:5489, in RunnableBindingBase.invoke(self, input, config, **kwargs)
   5482 @override
   5483 def invoke(
   5484     self,
   (...)   5487     **kwargs: Any | None,
   5488 ) -&amp;gt; Output:
-&amp;gt; 5489     return self.bound.invoke(
   5490         input,
   5491         self._merge_configs(config),
   5492         **{**self.kwargs, **kwargs},
   5493     )

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_core\language_models\chat_models.py:379, in BaseChatModel.invoke(self, input, config, stop, **kwargs)
    365 @override
    366 def invoke(
    367     self,
   (...)    372     **kwargs: Any,
    373 ) -&amp;gt; AIMessage:
    374     config = ensure_config(config)
    375     return cast(
    376         &quot;AIMessage&quot;,
    377         cast(
    378             &quot;ChatGeneration&quot;,
--&amp;gt; 379             self.generate_prompt(
    380                 [self._convert_input(input)],
    381                 stop=stop,
    382                 callbacks=config.get(&quot;callbacks&quot;),
    383                 tags=config.get(&quot;tags&quot;),
    384                 metadata=config.get(&quot;metadata&quot;),
    385                 run_name=config.get(&quot;run_name&quot;),
    386                 run_id=config.pop(&quot;run_id&quot;, None),
    387                 **kwargs,
    388             ).generations[0][0],
    389         ).message,
    390     )

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_core\language_models\chat_models.py:1088, in BaseChatModel.generate_prompt(self, prompts, stop, callbacks, **kwargs)
   1079 @override
   1080 def generate_prompt(
   1081     self,
   (...)   1085     **kwargs: Any,
   1086 ) -&amp;gt; LLMResult:
   1087     prompt_messages = [p.to_messages() for p in prompts]
-&amp;gt; 1088     return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_core\language_models\chat_models.py:903, in BaseChatModel.generate(self, messages, stop, callbacks, tags, metadata, run_name, run_id, **kwargs)
    900 for i, m in enumerate(input_messages):
    901     try:
    902         results.append(
--&amp;gt; 903             self._generate_with_cache(
    904                 m,
    905                 stop=stop,
    906                 run_manager=run_managers[i] if run_managers else None,
    907                 **kwargs,
    908             )
    909         )
    910     except BaseException as e:
    911         if run_managers:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_core\language_models\chat_models.py:1192, in BaseChatModel._generate_with_cache(self, messages, stop, run_manager, **kwargs)
   1190     result = generate_from_stream(iter(chunks))
   1191 elif inspect.signature(self._generate).parameters.get(&quot;run_manager&quot;):
-&amp;gt; 1192     result = self._generate(
   1193         messages, stop=stop, run_manager=run_manager, **kwargs
   1194     )
   1195 else:
   1196     result = self._generate(messages, stop=stop, **kwargs)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_openai\chat_models\base.py:1300, in BaseChatOpenAI._generate(self, messages, stop, run_manager, **kwargs)
   1298     if raw_response is not None and hasattr(raw_response, &quot;http_response&quot;):
   1299         e.response = raw_response.http_response  # type: ignore[attr-defined]
-&amp;gt; 1300     raise e
   1301 if (
   1302     self.include_response_headers
   1303     and raw_response is not None
   1304     and hasattr(raw_response, &quot;headers&quot;)
   1305 ):
   1306     generation_info = {&quot;headers&quot;: dict(raw_response.headers)}

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\langchain_openai\chat_models\base.py:1295, in BaseChatOpenAI._generate(self, messages, stop, run_manager, **kwargs)
   1288         return _construct_lc_result_from_responses_api(
   1289             response,
   1290             schema=original_schema_obj,
   1291             metadata=generation_info,
   1292             output_version=self.output_version,
   1293         )
   1294     else:
-&amp;gt; 1295         raw_response = self.client.with_raw_response.create(**payload)
   1296         response = raw_response.parse()
   1297 except Exception as e:

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\_legacy_response.py:364, in to_raw_response_wrapper.&amp;lt;locals&amp;gt;.wrapped(*args, **kwargs)
    360 extra_headers[RAW_RESPONSE_HEADER] = &quot;true&quot; 
    362 kwargs[&quot;extra_headers&quot;] = extra_headers
--&amp;gt; 364 return cast(LegacyAPIResponse[R], func(*args, **kwargs))

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\_utils\_utils.py:286, in required_args.&amp;lt;locals&amp;gt;.inner.&amp;lt;locals&amp;gt;.wrapper(*args, **kwargs)
    284             msg = f&quot;Missing required argument: {quote(missing[0])}&quot; 
    285     raise TypeError(msg)
--&amp;gt; 286 return func(*args, **kwargs)

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\resources\chat\completions\completions.py:1156, in Completions.create(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, prompt_cache_key, reasoning_effort, response_format, safety_identifier, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, verbosity, web_search_options, extra_headers, extra_query, extra_body, timeout)
   1110 @required_args([&quot;messages&quot;, &quot;model&quot;], [&quot;messages&quot;, &quot;model&quot;, &quot;stream&quot;])
   1111 def create(
   1112     self,
   (...)   1153     timeout: float | httpx.Timeout | None | NotGiven = not_given,
   1154 ) -&amp;gt; ChatCompletion | Stream[ChatCompletionChunk]:
   1155     validate_response_format(response_format)
-&amp;gt; 1156     return self._post(
   1157         &quot;/chat/completions&quot;,
   1158         body=maybe_transform(
   1159             {
   1160                 &quot;messages&quot;: messages,
   1161                 &quot;model&quot;: model,
   1162                 &quot;audio&quot;: audio,
   1163                 &quot;frequency_penalty&quot;: frequency_penalty,
   1164                 &quot;function_call&quot;: function_call,
   1165                 &quot;functions&quot;: functions,
   1166                 &quot;logit_bias&quot;: logit_bias,
   1167                 &quot;logprobs&quot;: logprobs,
   1168                 &quot;max_completion_tokens&quot;: max_completion_tokens,
   1169                 &quot;max_tokens&quot;: max_tokens,
   1170                 &quot;metadata&quot;: metadata,
   1171                 &quot;modalities&quot;: modalities,
   1172                 &quot;n&quot;: n,
   1173                 &quot;parallel_tool_calls&quot;: parallel_tool_calls,
   1174                 &quot;prediction&quot;: prediction,
   1175                 &quot;presence_penalty&quot;: presence_penalty,
   1176                 &quot;prompt_cache_key&quot;: prompt_cache_key,
   1177                 &quot;reasoning_effort&quot;: reasoning_effort,
   1178                 &quot;response_format&quot;: response_format,
   1179                 &quot;safety_identifier&quot;: safety_identifier,
   1180                 &quot;seed&quot;: seed,
   1181                 &quot;service_tier&quot;: service_tier,
   1182                 &quot;stop&quot;: stop,
   1183                 &quot;store&quot;: store,
   1184                 &quot;stream&quot;: stream,
   1185                 &quot;stream_options&quot;: stream_options,
   1186                 &quot;temperature&quot;: temperature,
   1187                 &quot;tool_choice&quot;: tool_choice,
   1188                 &quot;tools&quot;: tools,
   1189                 &quot;top_logprobs&quot;: top_logprobs,
   1190                 &quot;top_p&quot;: top_p,
   1191                 &quot;user&quot;: user,
   1192                 &quot;verbosity&quot;: verbosity,
   1193                 &quot;web_search_options&quot;: web_search_options,
   1194             },
   1195             completion_create_params.CompletionCreateParamsStreaming
   1196             if stream
   1197             else completion_create_params.CompletionCreateParamsNonStreaming,
   1198         ),
   1199         options=make_request_options(
   1200             extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
   1201         ),
   1202         cast_to=ChatCompletion,
   1203         stream=stream or False,
   1204         stream_cls=Stream[ChatCompletionChunk],
   1205     )

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\_base_client.py:1259, in SyncAPIClient.post(self, path, cast_to, body, options, files, stream, stream_cls)
   1245 def post(
   1246     self,
   1247     path: str,
   (...)   1254     stream_cls: type[_StreamT] | None = None,
   1255 ) -&amp;gt; ResponseT | _StreamT:
   1256     opts = FinalRequestOptions.construct(
   1257         method=&quot;post&quot;, url=path, json_data=body, files=to_httpx_files(files), **options
   1258     )
-&amp;gt; 1259     return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

File c:\Users\User\.conda\envs\Orange\Lib\site-packages\openai\_base_client.py:1014, in SyncAPIClient.request(self, cast_to, options, stream, stream_cls)
   1011         continue
   1013     log.debug(&quot;Raising connection error&quot;)
-&amp;gt; 1014     raise APIConnectionError(request=request) from err
   1016 log.debug(
   1017     'HTTP Response: %s %s &quot;%i %s&quot; %s',
   1018     request.method,
   (...)   1022     response.headers,
   1023 )
   1024 log.debug(&quot;request_id: %s&quot;, response.headers.get(&quot;x-request-id&quot;))

APIConnectionError: Connection error.
During task with name 'model' and id '165fa95b-????-????-????-????209ef8c5'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;☑️ 해결 방법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;C:\Users\User\.conda\envs\Orange\Lib\site-packages\httpx\_transports\default.py&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;모듈이 설치된 폴더로 이동하여 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;default.py를 아래와 같이 수정합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761628537226&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class HTTPTransport(BaseTransport):
    def __init__(
        self,
        verify: ssl.SSLContext | str | bool = True,
        cert: CertTypes | None = None,
        trust_env: bool = True,
        http1: bool = True,
        http2: bool = False,
        limits: Limits = DEFAULT_LIMITS,
        proxy: ProxyTypes | None = None,
        uds: str | None = None,
        local_address: str | None = None,
        retries: int = 0,
        socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
    ) -&amp;gt; None:
        import httpcore
        verify=False    # TODO 강제 False설정
        proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
        ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 생략 ~~~~~~~~~~~~~~~~~~~~~~~~~~~

class AsyncHTTPTransport(AsyncBaseTransport):
    def __init__(
        self,
        verify: ssl.SSLContext | str | bool = True,
        cert: CertTypes | None = None,
        trust_env: bool = True,
        http1: bool = True,
        http2: bool = False,
        limits: Limits = DEFAULT_LIMITS,
        proxy: ProxyTypes | None = None,
        uds: str | None = None,
        local_address: str | None = None,
        retries: int = 0,
        socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
    ) -&amp;gt; None:
        import httpcore
        verify=False    # TODO 강제 False설정
        proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
        ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
        
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 생략 ~~~~~~~~~~~~~~~~~~~~~~~~~~~&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>langchain</category>
      <category>Python</category>
      <category>SSL오류</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/31</guid>
      <comments>https://neneb-net.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 28 Oct 2025 14:16:01 +0900</pubDate>
    </item>
    <item>
      <title>Python @dataclass 데코레이터</title>
      <link>https://neneb-net.tistory.com/30</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;@dataclass 는 데이터 전용 클래스를 쉽게 만들 수 있도록 도와주는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;데코레이터입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 우리가 단순히 데이터를 담는 용도로 클래스를 정의할 때는 __init__, __repr__, __eq__ 같은 메서드를 일일이 작성해야 했습니다. 하지만 @dataclass를 사용하면 이런 메서드를 &lt;b&gt;자동 생성&lt;/b&gt;해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;gt; 기본 개념&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759240667064&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    active: bool = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;407&quot; data-start=&quot;378&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 파이썬이 자동으로 다음을 생성합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;556&quot; data-start=&quot;408&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;453&quot; data-start=&quot;408&quot;&gt;__init__ &amp;rarr; User(id, name, active) 자동 정의&lt;/li&gt;
&lt;li data-end=&quot;519&quot; data-start=&quot;454&quot;&gt;__repr__ &amp;rarr; User(id=1, name='Alice', active=True) 형태의 문자열 반환&lt;/li&gt;
&lt;li data-end=&quot;556&quot; data-start=&quot;520&quot;&gt;__eq__ &amp;rarr; 객체끼리 값 비교 가능 (== 연산자)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;gt; 사용 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759240694748&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user1 = User(1, &quot;Alice&quot;)
user2 = User(2, &quot;Bob&quot;, False)

print(user1)   # User(id=1, name='Alice', active=True)
print(user2)   # User(id=2, name='Bob', active=False)

print(user1 == User(1, &quot;Alice&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;&lt;b&gt;&amp;gt; 주요 옵션&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;856&quot; data-start=&quot;819&quot; data-ke-size=&quot;size16&quot;&gt;@dataclass 데코레이터는 여러 인자를 가질 수 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1079&quot; data-start=&quot;858&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;943&quot; data-start=&quot;858&quot;&gt;frozen=True&lt;br /&gt;&amp;rarr; 불변 객체(immutable)로 만듦 (값 변경 불가, dataclass 버전의 namedtuple 느낌)&lt;/li&gt;
&lt;li data-end=&quot;985&quot; data-start=&quot;944&quot;&gt;eq=True/False&lt;br /&gt;&amp;rarr; __eq__ 자동 생성 여부&lt;/li&gt;
&lt;li data-end=&quot;1036&quot; data-start=&quot;986&quot;&gt;order=True&lt;br /&gt;&amp;rarr; &amp;lt;, &amp;gt;, &amp;lt;=, &amp;gt;= 비교 연산 지원&lt;/li&gt;
&lt;li data-end=&quot;1079&quot; data-start=&quot;1037&quot;&gt;init=True/False&lt;br /&gt;&amp;rarr; __init__ 생성 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;gt; 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759240741496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@dataclass(frozen=True, order=True)
class Point:
    x: int
    y: int

p1 = Point(1, 2)
p2 = Point(2, 3)

print(p1 &amp;lt; p2)  # True (order=True 덕분에 지원)
# p1.x = 10  # 에러 발생 (frozen=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;&lt;b&gt;&amp;gt; field() 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드별로 세부 설정도 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1759240781320&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from dataclasses import dataclass, field

@dataclass
class Product:
    name: str
    price: float
    tags: list[str] = field(default_factory=list)  # 리스트는 반드시 default_factory 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 각 인스턴스가 &lt;b&gt;서로 다른 리스트 객체&lt;/b&gt;를 가지게 됩니다.&lt;br /&gt;(default=[]로 쓰면 모든 인스턴스가 같은 리스트 공유 &amp;rarr; 버그 발생 위험)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;------------------------------------------------- 일반 클래스와 비교 ------------------------------------------------- &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;gt; 일반 클래스 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1759240840449&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User:
    def __init__(self, id: int, name: str, active: bool = True):
        self.id = id
        self.name = name
        self.active = active

    def __repr__(self):
        return f&quot;User(id={self.id!r}, name={self.name!r}, active={self.active!r})&quot;

    def __eq__(self, other):
        if isinstance(other, User):
            return (self.id, self.name, self.active) == (other.id, other.name, other.active)
        return False


# 사용 예시
u1 = User(1, &quot;Alice&quot;)
u2 = User(1, &quot;Alice&quot;)

print(u1)        # User(id=1, name='Alice', active=True)
print(u1 == u2)  # True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 클래스는 __init__, __repr__, __eq__ 메서드를 &lt;b&gt;직접 작성&lt;/b&gt;해야 합니다.&lt;/p&gt;</description>
      <category>@dataclass</category>
      <category>Python</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/30</guid>
      <comments>https://neneb-net.tistory.com/30#entry30comment</comments>
      <pubDate>Tue, 30 Sep 2025 23:01:30 +0900</pubDate>
    </item>
    <item>
      <title>Opensource 게시판/포럼 솔루션</title>
      <link>https://neneb-net.tistory.com/29</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&amp;nbsp;Discourse&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;Ruby&amp;nbsp;on&amp;nbsp;Rails&amp;nbsp;+&amp;nbsp;Ember.js &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;&lt;b&gt;현대적인&amp;nbsp;UI&lt;/b&gt;,&amp;nbsp;&lt;b&gt;모바일&amp;nbsp;친화적&lt;/b&gt;,&amp;nbsp;소셜&amp;nbsp;네트워크&amp;nbsp;느낌의&amp;nbsp;토론&amp;nbsp;게시판. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;알림,&amp;nbsp;@멘션,&amp;nbsp;실시간&amp;nbsp;업데이트&amp;nbsp;지원 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;&lt;b&gt;플러그인/테마로&amp;nbsp;확장성&amp;nbsp;높음&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;SSO,&amp;nbsp;OAuth,&amp;nbsp;LDAP&amp;nbsp;등&amp;nbsp;연동&amp;nbsp;가능 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;최신형&amp;nbsp;커뮤니티,&amp;nbsp;기술&amp;nbsp;포럼,&amp;nbsp;고객&amp;nbsp;지원&amp;nbsp;Q&amp;amp;A&amp;nbsp;운영하려는&amp;nbsp;팀. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://www.discourse.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.discourse.org&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/discourse&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/discourse&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;NodeBB&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;Node.js&amp;nbsp;+&amp;nbsp;MongoDB/Redis &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;빠르고&amp;nbsp;반응성&amp;nbsp;좋은&amp;nbsp;포럼,&amp;nbsp;실시간&amp;nbsp;알림&amp;nbsp;및&amp;nbsp;WebSocket&amp;nbsp;기반. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;Slack/Discord&amp;nbsp;같은&amp;nbsp;느낌의&amp;nbsp;인터랙션 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;플러그인&amp;nbsp;마켓&amp;nbsp;풍부 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;Docker&amp;nbsp;설치&amp;nbsp;지원 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;채팅&amp;middot;SNS와&amp;nbsp;유사한&amp;nbsp;경험을&amp;nbsp;원하거나,&amp;nbsp;Node.js&amp;nbsp;환경에&amp;nbsp;익숙한&amp;nbsp;개발팀. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://nodebb.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodebb.org&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/NodeBB&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/NodeBB&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;Flarum&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;PHP&amp;nbsp;+&amp;nbsp;MySQL &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;미니멀하고&amp;nbsp;빠른&amp;nbsp;차세대&amp;nbsp;포럼.&amp;nbsp;&amp;ldquo;심플한&amp;nbsp;Discourse&amp;rdquo;&amp;nbsp;느낌. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;&lt;b&gt;확장&amp;nbsp;플러그인&amp;nbsp;생태계&amp;nbsp;존재&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;&lt;b&gt;모바일&amp;nbsp;대응&amp;nbsp;우수&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;&lt;b&gt;설치가&amp;nbsp;비교적&amp;nbsp;간단&lt;/b&gt;&amp;nbsp;(PHP&amp;nbsp;환경이면&amp;nbsp;OK) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;가볍고&amp;nbsp;심플한&amp;nbsp;커뮤니티를&amp;nbsp;빠르게&amp;nbsp;구축하려는&amp;nbsp;소규모&amp;nbsp;팀/동호회. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://flarum.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://flarum.org&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/flarum&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/flarum&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;4.&amp;nbsp;MyBB&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;PHP&amp;nbsp;+&amp;nbsp;MySQL &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;전통적인&amp;nbsp;게시판&amp;nbsp;스타일&amp;nbsp;(네이버&amp;nbsp;카페&amp;middot;phpBB&amp;nbsp;같은&amp;nbsp;느낌). &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;오래된&amp;nbsp;안정적인&amp;nbsp;프로젝트,&amp;nbsp;한글화&amp;nbsp;자료&amp;nbsp;풍부 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;&lt;b&gt;플러그인/테마&amp;nbsp;다양&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;전통적인&amp;nbsp;게시판&amp;nbsp;UI에&amp;nbsp;익숙한&amp;nbsp;사용자에게&amp;nbsp;친숙 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;학급/동호회&amp;nbsp;커뮤니티,&amp;nbsp;게임&amp;nbsp;길드&amp;nbsp;포럼&amp;nbsp;등&amp;nbsp;전통적&amp;nbsp;게시판&amp;nbsp;선호&amp;nbsp;시. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://mybb.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mybb.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/mybb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/mybb&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;5.&amp;nbsp;phpBB&lt;/b&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;PHP&amp;nbsp;+&amp;nbsp;MySQL/PostgreSQL &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;가장&amp;nbsp;오래된&amp;nbsp;오픈소스&amp;nbsp;포럼&amp;nbsp;중&amp;nbsp;하나.&amp;nbsp;커뮤니티&amp;nbsp;규모가&amp;nbsp;큼. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;방대한&amp;nbsp;플러그인/테마 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;유저&amp;nbsp;권한&amp;nbsp;관리가&amp;nbsp;매우&amp;nbsp;세밀함 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;대규모&amp;nbsp;포럼&amp;nbsp;운영에&amp;nbsp;검증된&amp;nbsp;솔루션 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;&lt;b&gt;대형&amp;nbsp;커뮤니티&lt;/b&gt;,&amp;nbsp;&lt;b&gt;장기간&amp;nbsp;안정적인&amp;nbsp;운영&lt;/b&gt;이&amp;nbsp;필요한&amp;nbsp;경우. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://www.phpbb.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.phpbb.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/phpbb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/phpbb&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;6.&amp;nbsp;Vanilla&amp;nbsp;Forums&amp;nbsp;(오픈소스&amp;nbsp;버전)&lt;/b&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;언어/기술:&amp;nbsp;PHP &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;특징:&amp;nbsp;&lt;b&gt;현대적인&amp;nbsp;UI&lt;/b&gt;&amp;nbsp;+&amp;nbsp;소셜&amp;nbsp;기능.&amp;nbsp;기업용&amp;nbsp;상용&amp;nbsp;버전도&amp;nbsp;존재. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;장점: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;깔끔한&amp;nbsp;인터페이스 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;기본&amp;nbsp;기능&amp;nbsp;충실 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;◦&amp;nbsp;커머스&amp;middot;서포트&amp;nbsp;포럼&amp;nbsp;운영에도&amp;nbsp;적합 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull;&amp;nbsp;추천&amp;nbsp;대상:&amp;nbsp;기업용&amp;nbsp;커뮤니티,&amp;nbsp;고객지원&amp;nbsp;포럼.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; 홈페이지: &lt;a href=&quot;https://open.vanillaforums.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://open.vanillaforums.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;bull; Git: &lt;a href=&quot;https://github.com/vanilla&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/vanilla&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 17.093%;&quot;&gt;&lt;b&gt; 솔루션 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 29.5349%;&quot;&gt;&lt;b&gt; &amp;nbsp;특징 요약 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 26.7441%;&quot;&gt;&lt;b&gt; &amp;nbsp;추천 대상 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 26.6279%;&quot;&gt;&lt;b&gt;기술&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;Discourse&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;&lt;b&gt;현대적,&lt;/b&gt; 실시간, &lt;b&gt;확장성&lt;/b&gt; 강력&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;기술 포럼, 고객지원&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;Ruby&amp;nbsp;on&amp;nbsp;Rails&amp;nbsp;+&amp;nbsp;Ember.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;NodeBB&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;Node.js 기반, &lt;b&gt;빠르고&lt;/b&gt; 채팅 느낌&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;개발팀, 실시간 커뮤니티&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;Node.js&amp;nbsp;+&amp;nbsp;MongoDB/Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;Flarum&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;&lt;b&gt;가볍고&lt;/b&gt; 미니멀, &lt;b&gt;모바일 최적화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;소규모 동호회, 심플 커뮤니티&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;PHP&amp;nbsp;+&amp;nbsp;MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;MyBB&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;전통 게시판 스타일, &lt;b&gt;한글화&lt;/b&gt; 용이&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;학급, 게임, 전통 포럼&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;PHP&amp;nbsp;+&amp;nbsp;MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;phpBB&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;검증된 대형 포럼 엔진&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;대형 커뮤니티 운영&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;PHP&amp;nbsp;+&amp;nbsp;MySQL/PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;&lt;b&gt;Vanilla Forums &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.5349%;&quot;&gt;기업/브랜드 커뮤니티 적합&lt;/td&gt;
&lt;td style=&quot;width: 26.7441%;&quot;&gt;기업 고객지원 포럼&lt;/td&gt;
&lt;td style=&quot;width: 26.6279%;&quot;&gt;PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Discourse</category>
      <category>Flarum</category>
      <category>MyBB</category>
      <category>NodeBB</category>
      <category>opensource 게시판</category>
      <category>phpbb</category>
      <category>Vanilla Forums</category>
      <category>오픈소스 게시판</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/29</guid>
      <comments>https://neneb-net.tistory.com/29#entry29comment</comments>
      <pubDate>Tue, 2 Sep 2025 20:57:14 +0900</pubDate>
    </item>
    <item>
      <title>Redmine의 대안으로 사용할 수 있는 Opensource 플랫폼 소개</title>
      <link>https://neneb-net.tistory.com/28</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1.&amp;nbsp;OpenProject&lt;/b&gt; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt; 특징: Gantt 차트, 칸반&amp;middot;스크럼 보드, 시간 추적, 이슈 및 문서 관리 등 종합 프로젝트 관리 기능 제공. 모던한 UI와 높은 보안성을 갖춘 온프레미스/클라우드 배포 모두 가능&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 장점: &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 사용자 친화적인 디자인과 상세한 온보딩/문서 지원&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 워크플로우 전반을 하나의 플랫폼에서 관리 가능&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 활용 사례: 건설업, 헬스케어 등 민감한 도메인에서도 선호됨&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 주의사항: 커뮤니티 에디션의 기능 제한과 다소 &lt;b&gt;높은 학습 곡선&lt;/b&gt; 존재&amp;nbsp; &lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 홈페이지: &lt;a href=&quot;https://www.openproject.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.openproject.org/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;Taiga&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 특징: 스크럼과 칸반 중심의 애자일 방식 프로젝트 관리에 특화된 시스템. 직관적인 UI와 가벼운 구조가 장점&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 장점: &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 백로그, 스프린트, 이슈 트래킹 등 &lt;b&gt;애자일 팀에 최적화된 기능&lt;/b&gt;&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;b&gt;쉬운 설치 및 다양한 언어 지원.&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 주의사항: 고급 프로젝트 관리 기능이나 복잡한 워크플로우에는 한계 있을 수 있음. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지: &lt;a href=&quot;https://taiga.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://taiga.io/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;3. Kanboard&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 특징: 초경량 칸반 보드 기반. 단순하고 효율적인 작업 시각화에 최적화된 도구&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 장점: &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;b&gt;드래그 앤 드롭 UI&lt;/b&gt;, 자동화, CLI 등 심플하지만 필요한 기능 제공. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 도커 환경에서도 쉽게 운영 가능. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 주의사항: 기능이 칸반 중심으로 제한적이며 모바일 앱은 없음 &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지: &lt;a href=&quot;https://kanboard.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kanboard.org/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;4.&amp;nbsp;Trac&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 특징: 이슈 추적, 버전 관리(&lt;b&gt;Git/SVN) 통합,&lt;/b&gt; 위키와 간단한 로드맵 기능을 제공하는 웹 기반 관리 시스템&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 장점: &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt; 프로젝트 일정, 티켓, 위키 간 하이퍼링크로 유기적 통합. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 타임라인, RSS/이메일 알림, &lt;b&gt;상세 권한 관리&lt;/b&gt; 기능 탑재. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 활용 사례: Django, FFmpeg, WordPress 등 다양한 오픈소스 프로젝트에서 사용&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 주의사항: UI가 다소 전통적이며, 현대적인 인터페이스를 선호하는 경우 아쉬울 수 있음. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지: &lt;a href=&quot;https://trac.edgewall.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://trac.edgewall.org/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;5.&amp;nbsp;ProjeQtOr&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 특징: '품질 중심 프로젝트 조직(Quality based Project Organizer)'이라는 슬로건으로, 리스크 관리, 예산/비용, 보고서, 워크플로우 기능 등 다양한 기능 제공&amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 장점: &lt;br /&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;◦&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 다국어 지원&lt;/b&gt;,&amp;nbsp;멀티&amp;nbsp;플랫폼. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 주의사항: 설정이 다소 복잡할 수 있으며, UI도 전통적일 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지: &lt;a href=&quot;https://www.projeqtor.org/en/product-en&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.projeqtor.org/en/product-en&lt;/a&gt;&lt;/span&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;&lt;b&gt;6. WorkLenz&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;특징: 오픈소스 워크플로우 관리 도구로, HR 및 CRM 모듈 포함. GitHub에서 꾸준히 활발하게 개발 중&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&amp;bull;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지: &lt;a href=&quot;https://worklenz.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://worklenz.com/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 17.7907%;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; &lt;span style=&quot;text-align: start;&quot;&gt;플랫폼&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 40.6977%;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; 특징 요약 &lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 41.3954%;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; 추천 대상 &lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;OpenProject&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;종합 기능 + 모던 UI&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;보안 및 기능성을 중시하는 조직&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;Taiga&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;애자일 중심, 직관적인 인터페이스&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;애자일팀, 스타트업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;Kanboard&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;간단 칸반 방식&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;심플한 작업 시각화가 필요한 소규모 팀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;Trac&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;이슈+버전+위키 통합&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;개발자 중심 프로젝트, 오픈소스 기반 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;ProjeQtOr&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;리스크/예산 관리 등 품질 중심 특화&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;예산/리스크 관리가 중요한 조직&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;&lt;b&gt;WorkLenz&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6977%;&quot;&gt;HR/CRM 모듈 통합 등 특별 기능 제공&lt;/td&gt;
&lt;td style=&quot;width: 41.3954%;&quot;&gt;다기능 커스터마이징이 필요한 팀&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>kanboard</category>
      <category>OpenProject</category>
      <category>opensource</category>
      <category>ProjeQtOr</category>
      <category>Redmine</category>
      <category>Taiga</category>
      <category>trac</category>
      <category>WorkLenz</category>
      <category>오픈소스</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/28</guid>
      <comments>https://neneb-net.tistory.com/28#entry28comment</comments>
      <pubDate>Tue, 2 Sep 2025 20:35:56 +0900</pubDate>
    </item>
    <item>
      <title>Grafana 에서 Business Charts의 캔들차트 표시하는 방법</title>
      <link>https://neneb-net.tistory.com/27</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Grafana에서 주식차트를 그리는 방법을 설명드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 구조만 맞춰주시고 javascript 코드는 그대로 사용하시면 아래와 같은 그래프가 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;차트&lt;/b&gt;&lt;/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;2107&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boo5Re/btsQcbRU9v4/5yPaD5tjCyBphMQvo1fA1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boo5Re/btsQcbRU9v4/5yPaD5tjCyBphMQvo1fA1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boo5Re/btsQcbRU9v4/5yPaD5tjCyBphMQvo1fA1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fboo5Re%2FbtsQcbRU9v4%2F5yPaD5tjCyBphMQvo1fA1k%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;2107&quot; height=&quot;733&quot; data-origin-width=&quot;2107&quot; data-origin-height=&quot;733&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;&lt;b&gt;데이터 구조&lt;/b&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;1281&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDE2B/btsQd7OAZO7/2XsMABKpBEBiBfuLuuVKG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDE2B/btsQd7OAZO7/2XsMABKpBEBiBfuLuuVKG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDE2B/btsQd7OAZO7/2XsMABKpBEBiBfuLuuVKG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDE2B%2FbtsQd7OAZO7%2F2XsMABKpBEBiBfuLuuVKG0%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;1281&quot; height=&quot;460&quot; data-origin-width=&quot;1281&quot; data-origin-height=&quot;460&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;&lt;b&gt;Javascript 코드입니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1756455701577&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const rawData = [];
context.panel.data.series.forEach((series) =&amp;gt; {
  const event_dates_Field = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;event_date&quot;));	// 날짜
  const openingPrices_Field = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;openingprice&quot;));	// 시가
  const tradePrices_Field = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;tradeprice&quot;));	// 종가
  const lowPrices_Field = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;lowprice&quot;));	// 저가
  const highPrices_Field = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;highprice&quot;));	// 고가
  if (!event_dates_Field || !openingPrices_Field || !tradePrices_Field || !lowPrices_Field || !highPrices_Field) return;

  const event_dates = event_dates_Field.values;
  const openingPrices = openingPrices_Field.values;
  const tradePrices = tradePrices_Field.values;
  const lowPrices = lowPrices_Field.values;
  const highPrices = highPrices_Field.values;

  for (let i = 0; i &amp;lt; event_dates.length; i++) {
    rawData.push([
      event_dates.get(i).toString(),
      openingPrices.get(i).toString(),
      tradePrices.get(i).toString(),
      lowPrices.get(i).toString(),
      highPrices.get(i).toString()
    ]);
  }
});
// console.log(rawData);
function calculateMA(dayCount, data) {
  var result = [];
  for (var i = 0, len = data.length; i &amp;lt; len; i++) {
    if (i &amp;lt; dayCount) {
      result.push('-');
      continue;
    }
    var sum = 0;
    for (var j = 0; j &amp;lt; dayCount; j++) {
      sum += +data[i - j][1];
    }
    result.push(sum / dayCount);
  }
  return result;
}
const dates = rawData.map(function (item) {
  return item[0];	// 날짜
});
const values = rawData.map(function (item) {
  return [+item[1], +item[2], +item[3], +item[4]];	// 시가(1), 종가(2), 저가(3), 고가(4)
});

return {
  legend: {
    data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30'],
    inactiveColor: '#777'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      animation: false,
      type: 'cross',
      lineStyle: {
        color: '#376df4',
        width: 2,
        opacity: 1
      }
    }
  },
  xAxis: {
    type: 'category',
    data: dates,
    axisLine: { lineStyle: { color: '#8392A5' } }
  },
  yAxis: {
    scale: true,
    axisLine: { lineStyle: { color: '#8392A5' } },
    splitLine: { show: false }
  },
  grid: {
    bottom: 80
  },
  dataZoom: [
    {
      textStyle: {
        color: '#8392A5'
      },
      handleIcon:
        'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
      dataBackground: {
        areaStyle: {
          color: '#8392A5'
        },
        lineStyle: {
          opacity: 0.8,
          color: '#8392A5'
        }
      },
      brushSelect: true
    },
    {
      type: 'inside'
    }
  ],
  series: [
    {
      type: 'candlestick',
      name: 'Day',
      data: values,
      itemStyle: {
        color: '#FD1050',
        color0: '#0CF49B',
        borderColor: '#FD1050',
        borderColor0: '#0CF49B'
      }
    },
    {
      name: 'MA5',
      type: 'line',
      data: calculateMA(5, values),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA10',
      type: 'line',
      data: calculateMA(10, values),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA20',
      type: 'line',
      data: calculateMA(20, values),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA30',
      type: 'line',
      data: calculateMA(30, values),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    }
  ]
};&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;</description>
      <category>business charts</category>
      <category>grafana</category>
      <category>캔들차트</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/27</guid>
      <comments>https://neneb-net.tistory.com/27#entry27comment</comments>
      <pubDate>Fri, 29 Aug 2025 17:27:35 +0900</pubDate>
    </item>
    <item>
      <title>dbt 명칭을 왜 소문자로 표기할까?</title>
      <link>https://neneb-net.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사실 &lt;b&gt;dbt&lt;/b&gt;는 정식 명칭이 &amp;ldquo;&lt;b&gt;data build tool&lt;/b&gt;&amp;rdquo;인데, dbt Labs에서는 일부러 &lt;b&gt;소문자 표기(db t)&lt;/b&gt;를 고수합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이유&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 브랜딩 철학&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;220&quot; data-start=&quot;146&quot;&gt;dbt Labs는 &amp;ldquo;데이터팀을 위한 도구가 거창한 이름이 아니라, &lt;b&gt;가볍고 실용적인 툴&lt;/b&gt;&amp;rdquo;이라는 이미지를 주고 싶었습니다.&lt;/li&gt;
&lt;li data-end=&quot;292&quot; data-start=&quot;224&quot;&gt;그래서 일반적인 약자처럼 &amp;ldquo;DBT&amp;rdquo;라고 대문자로 쓰지 않고, &lt;b&gt;소문자 dbt&lt;/b&gt;라는 독특한 스타일을 선택했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 개발자 문화와 유사성 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;397&quot; data-start=&quot;318&quot;&gt;리눅스 명령어나 Python 패키지 이름처럼, &lt;b&gt;소문자 CLI(command-line interface) 툴&lt;/b&gt; 스타일을 따랐습니다.&lt;/li&gt;
&lt;li data-end=&quot;464&quot; data-start=&quot;401&quot;&gt;실제로 설치 후 실행할 때도 dbt run, dbt test처럼 &lt;b&gt;소문자 명령어&lt;/b&gt;를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;공식 가이드라인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;538&quot; data-start=&quot;487&quot;&gt;dbt Labs 공식 문서에서도 &amp;ldquo;항상 dbt라고 소문자로 작성할 것&amp;rdquo;을 권장합니다. &lt;a href=&quot;https://www.getdbt.com/dbt-assets/dbt-trademark-guidelines&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;가이드&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&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;954&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2JKG4/btsPYK6udq8/GIwlptMQUuYjd0YYqSvk71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2JKG4/btsPYK6udq8/GIwlptMQUuYjd0YYqSvk71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2JKG4/btsPYK6udq8/GIwlptMQUuYjd0YYqSvk71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2JKG4%2FbtsPYK6udq8%2FGIwlptMQUuYjd0YYqSvk71%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;954&quot; height=&quot;212&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;212&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 data-end=&quot;640&quot; data-start=&quot;542&quot;&gt;대문자 &lt;b&gt;DBT&lt;/b&gt;는 다른 의미(예: 심리치료에서 쓰이는 &lt;b&gt;Dialectical Behavior Therapy&lt;/b&gt;)와 혼동될 수 있기 때문에 차별화 목적도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DBT</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/26</guid>
      <comments>https://neneb-net.tistory.com/26#entry26comment</comments>
      <pubDate>Tue, 19 Aug 2025 10:25:59 +0900</pubDate>
    </item>
    <item>
      <title>ETL vs ELT 비교</title>
      <link>https://neneb-net.tistory.com/25</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ETL ( Extract &amp;rarr; Transform &amp;rarr; Load )&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;li&gt;데이터를 원본에서 &lt;b&gt;추출(Extract)&lt;/b&gt; &amp;rarr; &lt;b&gt;중간 처리 서버에서 변환(Transform)&lt;/b&gt; &amp;rarr; 최종적으로 &lt;b&gt;데이터 웨어하우스에 적재(Load)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;데이터 웨어하우스의 연산 능력이 부족하던 시절에 주로 사용됨&lt;/li&gt;
&lt;li&gt;[Source]&amp;nbsp;&amp;rarr;&amp;nbsp;(Extract)&amp;nbsp;&amp;rarr;&amp;nbsp;[ETL&amp;nbsp;서버에서&amp;nbsp;Transform]&amp;nbsp;&amp;rarr;&amp;nbsp;(Load)&amp;nbsp;&amp;rarr;&amp;nbsp;[Data&amp;nbsp;Warehouse] &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ELT ( Extract &amp;rarr; Load &amp;rarr; Transform )&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;현대적 방식&lt;/b&gt; (dbt가 여기에 해당)&lt;/li&gt;
&lt;li&gt;데이터를 원본에서 &lt;b&gt;추출(Extract)&lt;/b&gt; &amp;rarr; 그대로 &lt;b&gt;데이터 웨어하우스에 적재(Load)&lt;/b&gt; &amp;rarr; 웨어하우스 내부에서 &lt;b&gt;SQL 기반 변환(Transform)&lt;/b&gt; 수행&lt;/li&gt;
&lt;li&gt;Snowflake, BigQuery, Redshift, Databricks 등 &lt;b&gt;강력한 클라우드 DWH&lt;/b&gt;가 등장하면서 표준이 됨&lt;/li&gt;
&lt;li&gt;[Source]&amp;nbsp;&amp;rarr;&amp;nbsp;(Extract&amp;nbsp;+&amp;nbsp;Load,&amp;nbsp;Ingestion)&amp;nbsp;&amp;rarr;&amp;nbsp;[Data&amp;nbsp;Warehouse]&amp;nbsp;&amp;rarr;&amp;nbsp;(Transform,&amp;nbsp;dbt)&amp;nbsp;&amp;rarr;&amp;nbsp;[Analytics-ready&amp;nbsp;Data]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;ETL&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;ELT (dbt 활용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Transform 위치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ETL 서버(외부 처리 엔진)&lt;/td&gt;
&lt;td&gt;데이터 웨어하우스 내부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 적재 순서&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;변환 후 Load&lt;/td&gt;
&lt;td&gt;Load 후 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;도구 예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Informatica, Talend, Pentaho&lt;/td&gt;
&lt;td&gt;&lt;b&gt;dbt&lt;/b&gt;, SQL, BigQuery/Snowflake/Redshift 내장 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;- 웨어하우스 부담 &amp;darr;- 보안&amp;middot;정책 제약 환경에서 유리&lt;/td&gt;
&lt;td&gt;- 웨어하우스의 강력한 성능 활용- 단순 구조 (원시데이터와 정제데이터 모두 보관)- SQL만으로 개발 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;- 변환 서버 운영 필요- 확장성 낮음&lt;/td&gt;
&lt;td&gt;- 웨어하우스 비용 증가 가능- 웨어하우스 의존도 &amp;uarr;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;과거 온프레미스 DWH 환경&lt;/td&gt;
&lt;td&gt;최신 클라우드 DWH + dbt 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DBT</category>
      <category>elt</category>
      <category>ETL</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/25</guid>
      <comments>https://neneb-net.tistory.com/25#entry25comment</comments>
      <pubDate>Tue, 19 Aug 2025 10:17:56 +0900</pubDate>
    </item>
    <item>
      <title>Ingestion vs Transformation 비교</title>
      <link>https://neneb-net.tistory.com/24</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 98.9536%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 9.65116%;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 47.4419%;&quot;&gt;&lt;b&gt; Ingestion &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 41.8653%;&quot;&gt;&lt;b&gt; Transformation (dbt) &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;원천(Source) 시스템에서 데이터를 수집하여 웨어하우스/데이터레이크에 &lt;b&gt;옮겨 적재&lt;/b&gt;하는 과정&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;웨어하우스 내부에 적재된 원시(raw) 데이터를 &lt;b&gt;정제&amp;middot;모델링&amp;middot;가공&lt;/b&gt;하는 과정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;ETL/ELT 단계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;E(Extract) + L(Load)&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;T(Transform)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;주요 도구&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;Fivetran, Stitch, Airbyte, Spark, Python 스크립트 등&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;&lt;b&gt;dbt Core / dbt Cloud&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;출력 결과&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;웨어하우스의 &lt;b&gt;Landing / Raw Layer&lt;/b&gt; (원시 데이터)&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;웨어하우스의 &lt;b&gt;Staging / Warehouse / Mart Layer&lt;/b&gt; (분석&amp;middot;비즈니스 친화적 데이터)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;기술적 초점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;- 데이터 이동 자동화- API, DB 커넥터, 스트리밍 등 지원- 스키마나 포맷 변환 최소화&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;- SQL 기반 모델링- 데이터 품질 검증(Test)- 의존성 관리(DAG)- 문서화 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;사용자&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;데이터 엔지니어 중심&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;데이터 분석가 + 애널리틱스 엔지니어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.65116%;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&amp;ldquo;Facebook Ads 데이터를 Snowflake로 매일 적재&amp;rdquo;&lt;/td&gt;
&lt;td style=&quot;width: 41.8653%;&quot;&gt;&amp;ldquo;Snowflake에 쌓인 Ads 데이터를 정제해 마케팅 ROI 리포트용 테이블 생성&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>elt</category>
      <category>ETL</category>
      <category>Ingestion</category>
      <category>Transformation</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/24</guid>
      <comments>https://neneb-net.tistory.com/24#entry24comment</comments>
      <pubDate>Tue, 19 Aug 2025 10:12:16 +0900</pubDate>
    </item>
    <item>
      <title>주가 이동평균 구하는 Query (MySQL, Mariadb)</title>
      <link>https://neneb-net.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주가 데이터 와 이동평균을 Grafana의 차트에 표시해주기위환 MySQL 쿼리 입니다.&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;이동평균을 주가 뿐만 아니라 DAU등에 접목해도 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 데이터 구조&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 46.8605%;&quot; border=&quot;1&quot; width=&quot;262&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 17.907%;&quot; width=&quot;87&quot; height=&quot;23&quot;&gt;&lt;b&gt;event_date&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.1861%;&quot; width=&quot;99&quot;&gt;&lt;b&gt;symbol_code&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 14.7675%;&quot; width=&quot;76&quot;&gt;&lt;b&gt;tradePrice&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-18&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;187,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-21&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;187,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-22&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;193,700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-23&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;192,900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-24&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;191,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-25&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;193,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-28&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;195,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;196,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;200,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-05-02&lt;/td&gt;
&lt;td style=&quot;width: 14.1861%; text-align: center;&quot;&gt;35420&lt;/td&gt;
&lt;td style=&quot;width: 14.7675%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;197,400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754544461629&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set @symbol_code = '035420';    -- 종목 코드
set @interval_day = 100;        -- 현재일 기준 조회 기간
with sub as (
    select event_date, tradePrice, day_5_moving_avg, day_10_moving_avg, day_20_moving_avg, day_60_moving_avg
    from (
        SELECT event_date, symbol_code, tradePrice
            , ROUND(AVG(tradePrice)OVER (ORDER BY event_date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW), 2) AS day_5_moving_avg  -- 5일 이동 평균
            , ROUND(AVG(tradePrice)OVER (ORDER BY event_date ROWS BETWEEN 9 PRECEDING AND CURRENT ROW), 2) AS day_10_moving_avg  -- 10일 이동 평균
            , ROUND(AVG(tradePrice)OVER (ORDER BY event_date ROWS BETWEEN 19 PRECEDING AND CURRENT ROW), 2) AS day_20_moving_avg  -- 20일 이동 평균
            , ROUND(AVG(tradePrice)OVER (ORDER BY event_date ROWS BETWEEN 59 PRECEDING AND CURRENT ROW), 2) AS day_60_moving_avg  -- 60일 이동 평균
        FROM tbl_stock_list_kor
        WHERE event_date &amp;gt;= date_add(current_date, interval (abs(@interval_day) + 100) * -1 day)
            and event_date &amp;lt;= current_date
            and symbol_code = @symbol_code
    ) as ta
    where event_date &amp;gt;= date_add(current_date, interval abs(@interval_day) * -1 day) and event_date &amp;lt;= current_date
)
select cast(ta.event_date as char(10)) as event_date
    , (case tb.rno when 1 then '주가'
                when 2 then '5일선'
                when 3 then '10일선'
                when 4 then '20일선'
                when 5 then '60일선'
                end) as legend
, (case tb.rno when 1 then ta.tradePrice
                when 2 then ta.day_5_moving_avg
                when 3 then ta.day_10_moving_avg
                when 4 then ta.day_20_moving_avg
                when 5 then ta.day_60_moving_avg
                end) as price
from sub as ta
cross join (select 1 rno union select 2 union select 3 union select 4 union select 5) as tb
order by event_date asc, legend asc;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 쿼리 실행 결과&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 40.4645%;&quot; border=&quot;1&quot; width=&quot;217&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; width=&quot;87&quot; height=&quot;23&quot;&gt;&lt;b&gt;event_date&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot; width=&quot;56&quot;&gt;&lt;b&gt;legend&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: center;&quot; width=&quot;74&quot;&gt;&lt;b&gt;price&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;10일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;190,660&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;20일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;189,630&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;5일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;193,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;60일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;206,630&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-29&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;주가&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;196,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;10일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;192,270&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;20일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;189,875&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;5일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;195,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;60일선&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;206,330&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3022%; text-align: center;&quot; height=&quot;23&quot;&gt;2025-04-30&lt;/td&gt;
&lt;td style=&quot;width: 9.09237%; text-align: center;&quot;&gt;주가&lt;/td&gt;
&lt;td style=&quot;width: 8.76037%; text-align: right;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;200,500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ Chart&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NP7h1/btsPJ3fd89M/JgpKgUMLjJK02pz8sGkT6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NP7h1/btsPJ3fd89M/JgpKgUMLjJK02pz8sGkT6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NP7h1/btsPJ3fd89M/JgpKgUMLjJK02pz8sGkT6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNP7h1%2FbtsPJ3fd89M%2FJgpKgUMLjJK02pz8sGkT6k%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;2018&quot; height=&quot;547&quot; data-origin-width=&quot;2018&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>이동평균 쿼리</category>
      <category>주가</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/23</guid>
      <comments>https://neneb-net.tistory.com/23#entry23comment</comments>
      <pubDate>Thu, 7 Aug 2025 14:37:52 +0900</pubDate>
    </item>
    <item>
      <title>DAU 데이터의 z-score 계산 Query</title>
      <link>https://neneb-net.tistory.com/22</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; &lt;b&gt;DAU 데이터 구조&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 38.4869%; height: 136px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;event_date&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;dau&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; height: 17px; text-align: center;&quot;&gt;2025-07-01&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; height: 17px; text-align: right;&quot;&gt;12,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; height: 17px; text-align: center;&quot;&gt;2025-07-02&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; height: 17px; text-align: right;&quot;&gt;13,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; height: 17px; text-align: center;&quot;&gt;2025-07-03&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; height: 17px; text-align: right;&quot;&gt;15,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; height: 17px; text-align: center;&quot;&gt;2025-07-04&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; height: 17px; text-align: right;&quot;&gt;13,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; height: 17px; text-align: center;&quot;&gt;2025-07-05&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; height: 17px; text-align: right;&quot;&gt;16,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; text-align: center; height: 17px;&quot;&gt;2025-07-06&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; text-align: right; height: 17px;&quot;&gt;15,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.3879%; text-align: center; height: 17px;&quot;&gt;2025-07-07&lt;/td&gt;
&lt;td style=&quot;width: 14.008%; text-align: right; height: 17px;&quot;&gt;19,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;gt; &lt;b&gt;DAU 및 Z-Score 조회 쿼리&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753834284916&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    event_date,
    dau,
    ROUND((dau - stats.avg_dau) / stats.std_dau, 4) AS dau_zscore
FROM
    tbl_dau as ta,
    (
        SELECT
            AVG(dau) AS avg_dau,
            STDDEV(dau) AS std_dau
        FROM tbl_dau
        where event_date &amp;gt;= '2025-07-01'
            and event_date &amp;lt; '2025-07-11'
    ) AS stats
where ta.event_date &amp;gt;= '2025-07-01'
    and ta.event_date &amp;lt; '2025-07-11'
ORDER BY event_date;&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;gt; &lt;b&gt;조회 결과&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 65.0007%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.48404%; text-align: center;&quot;&gt;&lt;b&gt;event_date&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; text-align: center;&quot;&gt;&lt;b&gt;dau&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: center;&quot;&gt;&lt;b&gt;dau_zscore&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 6.48404%; height: 17px; text-align: center;&quot;&gt;2025-07-01&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; height: 17px; text-align: right;&quot;&gt;12,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;-1.3044&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 6.48404%; height: 17px; text-align: center;&quot;&gt;2025-07-02&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; height: 17px; text-align: right;&quot;&gt;13,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;-0.8361&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 6.48404%; height: 17px; text-align: center;&quot;&gt;2025-07-03&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; height: 17px; text-align: right;&quot;&gt;15,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;0.1003&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 6.48404%; height: 17px; text-align: center;&quot;&gt;2025-07-04&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; height: 17px; text-align: right;&quot;&gt;13,500&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;-0.602&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 6.48404%; height: 17px; text-align: center;&quot;&gt;2025-07-05&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; height: 17px; text-align: right;&quot;&gt;16,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;0.5686&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.48404%; text-align: center;&quot;&gt;2025-07-06&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; text-align: right;&quot;&gt;15,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;0.1003&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 6.48404%; text-align: center;&quot;&gt;2025-07-07&lt;/td&gt;
&lt;td style=&quot;width: 5.22599%; text-align: right;&quot;&gt;19,000&lt;/td&gt;
&lt;td style=&quot;width: 3.04879%; text-align: right;&quot;&gt;1.9733&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>Dau</category>
      <category>Z-SCORE</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/22</guid>
      <comments>https://neneb-net.tistory.com/22#entry22comment</comments>
      <pubDate>Wed, 30 Jul 2025 09:15:56 +0900</pubDate>
    </item>
    <item>
      <title>머신러닝 모델 유형별 대표적인 평가 지표</title>
      <link>https://neneb-net.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;gt; 분류( Classification ) 모델 평가 지표&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;지표명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;특징/용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Accuracy (정확도)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전체 중 맞게 예측한 비율&lt;/td&gt;
&lt;td&gt;클래스 균형이 잘 맞을 때 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Precision (정밀도)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;양성 예측 중 실제 양성 비율&lt;/td&gt;
&lt;td&gt;FP(거짓양성) 줄일 때 중요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Recall (재현율)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실제 양성 중 맞게 예측한 비율&lt;/td&gt;
&lt;td&gt;FN(거짓음성) 줄일 때 중요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;F1 Score&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정밀도와 재현율의 조화평균&lt;/td&gt;
&lt;td&gt;정밀도와 재현율 균형 필요할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ROC-AUC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;거짓양성비율 대비 참양성비율 곡선 아래 면적&lt;/td&gt;
&lt;td&gt;임계값 무관, 클래스 불균형 문제에 강함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PR-AUC (P-R 곡선 아래 면적)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정밀도와 재현율 곡선 아래 면적&lt;/td&gt;
&lt;td&gt;불균형 데이터에서 성능 평가에 더 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Confusion Matrix&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;TP, FP, TN, FN 행렬 형태로 예측 결과 요약&lt;/td&gt;
&lt;td&gt;다양한 지표 계산의 기본 자료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Log Loss (Cross-Entropy)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;확률 예측과 실제값 간 차이&lt;/td&gt;
&lt;td&gt;확률 기반 분류 모델 성능 평가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MCC (Matthews Correlation Coefficient)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;불균형 데이터에서 균형 잡힌 평가 지표&lt;/td&gt;
&lt;td&gt;데이터 불균형 심할 때 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;size18&quot;&gt;&lt;b&gt;&amp;gt; 회귀 (Regression) 모델 평가 지표 &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;지표명&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; 특징/용도 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MSE (Mean Squared Error)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오차 제곱의 평균&lt;/td&gt;
&lt;td&gt;큰 오차에 민감&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RMSE (Root MSE)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MSE의 제곱근&lt;/td&gt;
&lt;td&gt;단위가 원래 값과 같아 해석 용이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MAE (Mean Absolute Error)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;절대 오차 평균&lt;/td&gt;
&lt;td&gt;이상치에 덜 민감&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MAPE (Mean Absolute Percentage Error)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;절대 오차를 실제 값으로 나눈 비율의 평균 (백분율)&lt;/td&gt;
&lt;td&gt;오차를 %로 직관적으로 표현, 0에 가까운 값 주의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;R&amp;sup2; (결정계수)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모델이 데이터 분산을 얼마나 설명하는지 비율&lt;/td&gt;
&lt;td&gt;1에 가까울수록 좋은 설명력&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;size18&quot;&gt;&lt;b&gt;&amp;gt; 클러스터링 (Clustering) 평가 지표 &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; &lt;b&gt;지표명&lt;/b&gt; &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;특징/용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Silhouette Score&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;군집 간 거리와 군집 내 거리 비율&lt;/td&gt;
&lt;td&gt;1에 가까울수록 군집 품질 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Davies-Bouldin Index&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;군집 간 거리 대비 군집 내 거리 비율&lt;/td&gt;
&lt;td&gt;값이 작을수록 군집 품질 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Calinski-Harabasz Index&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;군집 간 분산 대비 군집 내 분산 비율&lt;/td&gt;
&lt;td&gt;값이 클수록 군집 품질 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>머신러닝 평가지표</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/21</guid>
      <comments>https://neneb-net.tistory.com/21#entry21comment</comments>
      <pubDate>Fri, 25 Jul 2025 17:30:13 +0900</pubDate>
    </item>
    <item>
      <title>Grafana Business Chart 누적형 그래프 그리기</title>
      <link>https://neneb-net.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Grafana에서 Business Chart를 사용해서 누적형 그래프를 그리는 방법을 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;bull; 데이터 구조&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 56.1608%;&quot; border=&quot;1&quot; width=&quot;214&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; width=&quot;70&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;legend&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot; width=&quot;70&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;xvalue&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20.1801%;&quot; width=&quot;74&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;yvalue&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-01&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;138,938&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;B&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-01&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;31,065&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;C&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-01&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;9,509&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;D&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-01&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;22,073&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-02&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp; &lt;/span&gt;121,677&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;B&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-02&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;31,899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;C&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-02&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;12,903&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 14.702%;&quot; height=&quot;23&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;D&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.8449%;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2074-02&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: right; width: 20.1801%;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;21,503&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;bull; 그래프&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osOOu/btsOYfuLLG6/C9U8HqBotewgir0ysFpNA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osOOu/btsOYfuLLG6/C9U8HqBotewgir0ysFpNA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osOOu/btsOYfuLLG6/C9U8HqBotewgir0ysFpNA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FosOOu%2FbtsOYfuLLG6%2FC9U8HqBotewgir0ysFpNA0%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;1207&quot; height=&quot;408&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;bull; Script 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751342940531&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dataMap = {}; 							// 시즌별 데이터 저장용
const xLabelsSet = new Set(); 	// x축 레벨 값들
let minYValue = Infinity;      			// y축 최소값 계산용

context.panel.data.series.forEach((series) =&amp;gt; {
  const legendField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;legend&quot;));		// 범례
  const xvalueField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;xvalue&quot;));			// x축에 표시할 값
  const yvalueField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;yvalue&quot;));			// y축에 표시할 값
  if (!legendField || !xvalueField || !yvalueField) return;

  const legends = legendField.values;		// 범례 배열
  const xvalues = xvalueField.values;		// x축 값 배열
  const values = yvalueField.values;			// y축 값 배열

  for (let i = 0; i &amp;lt; legends.length; i++) {
    const legend = legends.get(i);
    const xvalue = xvalues.get(i);
    const yvalue = values.get(i);
    xLabelsSet.add(xvalue);

    if (!dataMap[legend]) {
      dataMap[legend] = {};
    }
    dataMap[legend][xvalue] = yvalue;

    // 최소값 갱신
    if (typeof yvalue === 'number' &amp;amp;&amp;amp; yvalue &amp;lt; minYValue) {
      minYValue = yvalue;
    }
  }
});

// x축 정렬된 레벨 값들 (문자열 기준 정렬)
const xLabels = Array.from(xLabelsSet).sort((a, b) =&amp;gt; a - b);

// 시리즈 생성
const series = Object.keys(dataMap).map(legend =&amp;gt; {
  const xValueMap = dataMap[legend];
  const displayData = xLabels.map(xvalue =&amp;gt; xValueMap[xvalue] ?? null);
  // 최고값 찾기
  let maxIndex = -1;
  let maxValue = -Infinity;
  displayData.forEach((v, i) =&amp;gt; {
    if (typeof v === 'number' &amp;amp;&amp;amp; v &amp;gt; maxValue) {
      maxValue = v;
      maxIndex = i;
    }
  });

  return {
    name: legend,
    type: &quot;line&quot;,
    stack: &quot;total&quot;,
    data: displayData,
    symbolSize: 4,    // 점의 크기
    areaStyle: {
      opacity: 0.3    // 투명도 조절 (0 ~ 1 사이 값)
    },
  };
});

return {
  grid: {
    bottom: &quot;4.5%&quot;,
    containLabel: true,
    left: &quot;3%&quot;,
    right: &quot;4%&quot;
  },
  tooltip: {
    trigger: &quot;item&quot;,
    axisPointer: {
      type: 'cross',
    },
    formatter: function (params) {
      // 숫자 포맷: 천단위 구분
      const value = typeof params.data === 'number'
        ? params.data.toLocaleString('ko-KR')
        : params.data;
      return `
        ${params.marker}
        &amp;lt;strong style=&quot;color:${params.color}&quot;&amp;gt;${params.seriesName}&amp;lt;/strong&amp;gt;: ${value}
      `;
    }
  },
  legend: {
    name: '시즌',
    data: Object.keys(dataMap)
  },
  xAxis: {  // x축 설정
    name: '',           // 축 이름 텍스트.
    nameLocation: &quot;middle&quot;, // &quot;start&quot;, &quot;middle&quot;, &quot;end&quot; 가능. 이름의 위치 지정.
    nameGap: 25,            // 축 이름과 축 간의 거리(px)
    nameTextStyle: {        // 폰트 크기, 두께 등 스타일 설정
      fontSize: 14,
      fontWeight: 'bold'
    },
    boundaryGap: false,
    type: &quot;category&quot;,
    data: xLabels, 
    axisLabel: {
      margin: 25,       // 레이블과 축 간 거리
      fontSize: 13      // &amp;larr; 글씨 크기 설정 (예: 12px)
    }
  },
  yAxis: {  // y축 설정
    name: '',
    nameTextStyle: {
      fontSize: 14,
      fontWeight: 'bold'
    },
    type: &quot;value&quot;,
    min: (minYValue * 0.85).toFixed(0)
  },
  series
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>business chart</category>
      <category>grafana</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/20</guid>
      <comments>https://neneb-net.tistory.com/20#entry20comment</comments>
      <pubDate>Tue, 1 Jul 2025 13:09:43 +0900</pubDate>
    </item>
    <item>
      <title>Grafana Business Charts Tooltip 가이드</title>
      <link>https://neneb-net.tistory.com/19</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Grafana에서 Business Charts를 사용할 때 Tooltip에 표시되는 내용을 정렬하는 방법을 설명드리겠습니다.&lt;/h3&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;bull; 적용 전 / 후의 모습입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brwcTA/btsOkzGB90g/JK4oBodutBFPhjrFMYKNak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brwcTA/btsOkzGB90g/JK4oBodutBFPhjrFMYKNak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brwcTA/btsOkzGB90g/JK4oBodutBFPhjrFMYKNak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrwcTA%2FbtsOkzGB90g%2FJK4oBodutBFPhjrFMYKNak%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;1174&quot; height=&quot;521&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; tooltip 옵션의 코드를 아래와 같이 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;background-color: #ffffff;&quot;&gt;&lt;code&gt;tooltip: {
  trigger: &quot;axis&quot;,
  formatter: function (params) {
    // 수치를 천 단위로 변환하는 함수
    const formatNumber = num =&amp;gt; {
      if (num == null || isNaN(num)) return '-';
      return num.toLocaleString();  // 또는: String(num).replace(/\B(?=(\d{3})+(?!\d))/g, &quot;,&quot;)
    };

    const sortedParams = params.slice().sort((a, b) =&amp;gt; {
      const aVal = a.data != null ? a.data : -Infinity;
      const bVal = b.data != null ? b.data : -Infinity;
      return bVal - aVal;
    });

    let text = `${params[0].axisValue}&amp;lt;br/&amp;gt;`;
    sortedParams.forEach(param =&amp;gt; {
      text += `
        &amp;lt;span style=&quot;display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:${param.color};&quot;&amp;gt;&amp;lt;/span&amp;gt;
        ${param.seriesName}: ${formatNumber(param.data)}&amp;lt;br/&amp;gt;
      `;
    });
    return text;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>business chart</category>
      <category>grafana</category>
      <category>grafana business charts</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/19</guid>
      <comments>https://neneb-net.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 30 May 2025 15:22:57 +0900</pubDate>
    </item>
    <item>
      <title>Grafana Business Charts 사용 가이드</title>
      <link>https://neneb-net.tistory.com/18</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Grafana에서 서드파티 차트중 Business Charts를 사용하는 방법을 알려드리겠습니다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;0. legend / xvalue / yvalue 컬럼으로 이루어진 데이터를 준비합니다.&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VPKQe/btsOjr2AmFE/tAngyCToCSnKK3KZ1u82Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VPKQe/btsOjr2AmFE/tAngyCToCSnKK3KZ1u82Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VPKQe/btsOjr2AmFE/tAngyCToCSnKK3KZ1u82Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVPKQe%2FbtsOjr2AmFE%2FtAngyCToCSnKK3KZ1u82Q0%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;454&quot; height=&quot;352&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;1. Business Charts 를 선택 &amp;gt; Editor Mode를 Visual로 선택합니다.&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;1107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eacpY4/btsOjvKHvGS/SqyLqdKA3eRaROXTeSKgF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eacpY4/btsOjvKHvGS/SqyLqdKA3eRaROXTeSKgF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eacpY4/btsOjvKHvGS/SqyLqdKA3eRaROXTeSKgF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeacpY4%2FbtsOjvKHvGS%2FSqyLqdKA3eRaROXTeSKgF1%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;656&quot; height=&quot;1107&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;1107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 아래 JavaScript 코드를 Visual Editor 탭의 Function에 붙여넣어 줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 코드에 대한 설명은 주석을 참고하시거나 ChatGPT에 코드를 붙여넣은 후 프로프트로 코드 수정을 질문하시면 꽤 정확한 답변을 얻으실 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748578055940&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dataMap = {}; 					  // 전체 데이터
const xLabelsSet = new Set(); 	// X축 라벨값 집합

// 데이터 집합을 생성합니다.
context.panel.data.series.forEach((series) =&amp;gt; {
  const legendField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;legend&quot;));		// 범례
  const xValueField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;xvalue&quot;));		//  X 축
  const yValueField = series.fields.find(f =&amp;gt; f.name.toLowerCase().includes(&quot;yvalue&quot;));		// Y 축
  if (!legendField || !xValueField || !yValueField) return;

  const legends = legendField.values;
  const xValues = xValueField.values;
  const yValues = yValueField.values;

  for (let i = 0; i &amp;lt; legends.length; i++) {
    const legend = legends.get(i);
    const xValue = xValues.get(i);
    const yValue = yValues.get(i);
    xLabelsSet.add(xValue);
    if (!dataMap[legend]) {
      dataMap[legend] = {};
    }
    dataMap[legend][xValue] = yValue;
  }
});

// X축에 표시할 Unique 라벨 배열
const xLabels = Array.from(xLabelsSet).sort((a, b) =&amp;gt; a - b);
// 시리즈 생성
const series = Object.keys(dataMap).map(legend =&amp;gt; {
  const xValueMap = dataMap[legend];
  const yValueMap = xLabels.map(xValue =&amp;gt; xValueMap[xValue] ?? null);
  return {
    name: legend,     // 범례에 속한 항목 이름
    type: &quot;line&quot;,     // 차트 타입(bar, line)
    stack: &quot;&quot;,
    data: yValueMap   // Y Value 데이터 배열
  };
});

return {
  grid: {
    top: &quot;30%&quot;,
    bottom: &quot;5%&quot;,  // 그래프 하단의 여백을 설정하는 효과가 있습니다.
    containLabel: true,
    left: &quot;3%&quot;,
    right: &quot;4%&quot;
  },
  tooltip: {
    trigger: &quot;axis&quot;
  },
  legend: { // 범례 설정
    type: 'plain', // plain, scroll
    name: '모델',
    data: Object.keys(dataMap),
    orient: 'horizontal',      // 수평 정렬
    top: 'top',
    left: 'center',
    itemGap: 10,               // 범례 항목 간 간격
    padding: [10, 20],         // 범례 영역 안쪽 여백
    width: '80%',              // 너비 제한을 주면 줄바꿈 발생 가능
    textStyle: {
      fontSize: 14
    }
  },
  xAxis: {  // x축 설정
    name: '',           // 축 이름 텍스트.
    nameLocation: &quot;middle&quot;, // &quot;start&quot;, &quot;middle&quot;, &quot;end&quot; 가능. 이름의 위치 지정.
    nameGap: 30,            // 축 이름과 축 간의 거리(px)
    nameTextStyle: {        // 폰트 크기, 두께 등 스타일 설정
      fontSize: 20,
      fontWeight: 'bold'
    },
    boundaryGap: true,
    type: &quot;category&quot;,
    data: xLabels,
    axisLabel: {
      fontSize: 14,
      rotate: 60,  // 라벨을 45도 기울임
      interval: 0 // 모든 항목 출력 (스크롤 지원에 필수)
    }
  },
  yAxis: {  // y축 설정
    name: '판매량',
    nameTextStyle: {
      fontSize: 14,
      fontWeight: 'bold'
    },
    type: &quot;value&quot;,
    interval: 100000  // 10 단위 간격 설정
  },
  series,
  dataZoom: [
    {
      type: 'slider',
      show: true,
      xAxisIndex: 0,
      start: 0,
      end: xLabels.length &amp;gt; 20 ? 50 : 100
    }
  ],
  graphic: [
    {
      type: 'text',
      left: 'left',
      bottom: '20',   // 'bottom'은 grid 바깥 기준이므로, 그래프 아래 적당한 픽셀 위치로 설정
      left: '70',   // 좌측 여백을 설정합니다.
    }
  ]
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 그래프의 출력 결과는 아래와 같습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfCIZn/btsOju59KpW/RhAhFWglZJeJFPPLfhKdC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfCIZn/btsOju59KpW/RhAhFWglZJeJFPPLfhKdC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfCIZn/btsOju59KpW/RhAhFWglZJeJFPPLfhKdC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfCIZn%2FbtsOju59KpW%2FRhAhFWglZJeJFPPLfhKdC1%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;1234&quot; height=&quot;529&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>business chart</category>
      <category>grafana</category>
      <category>grafana business charts</category>
      <author>aimler</author>
      <guid isPermaLink="true">https://neneb-net.tistory.com/18</guid>
      <comments>https://neneb-net.tistory.com/18#entry18comment</comments>
      <pubDate>Fri, 30 May 2025 13:18:50 +0900</pubDate>
    </item>
  </channel>
</rss>