<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>99duuk</title>
    <link>https://99duuk.tistory.com/</link>
    <description>김동우</description>
    <language>ko</language>
    <pubDate>Sun, 23 Aug 2026 00:31:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>99duuk</managingEditor>
    <image>
      <title>99duuk</title>
      <url>https://tistory1.daumcdn.net/tistory/6872459/attach/23ff339a0a1243b69396b5cb1a0f4e49</url>
      <link>https://99duuk.tistory.com</link>
    </image>
    <item>
      <title>2026년 4월 러닝 챌린지</title>
      <link>https://99duuk.tistory.com/271</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdVd2s/dJMcahqKoBY/t5GxPeNruwhkVnU4ZK2NI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdVd2s/dJMcahqKoBY/t5GxPeNruwhkVnU4ZK2NI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdVd2s/dJMcahqKoBY/t5GxPeNruwhkVnU4ZK2NI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdVd2s%2FdJMcahqKoBY%2Ft5GxPeNruwhkVnU4ZK2NI1%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;1179&quot; height=&quot;1362&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이번달은 300k 마일리지를 목표로 잡고..&amp;nbsp;&lt;br&gt;최대한 많이 달리려고 노력했다. 어째저째 300k가 채워지긴 했다.&lt;br&gt;한 200k 부터는 달리는게 너무너무 지루했고 달리기 너무너무 싫었다.&lt;br&gt;꺼드럭거리기 위해 참고 달렸다....&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;열심히 달리고 또 열심히 마셨다.. 술을 최대한 안먹는다 ... 라고 다짐했지만 마실 상황이 자주 발생했다.&amp;nbsp;&lt;br&gt;매일 10k 정도씩 잡고 달리다보니 몸 회복이 많이 더뎠다.&amp;nbsp;&lt;br&gt;육체가 참 나약하다.........&lt;br&gt;한 430페이스로 달린다고 생각하고 시곌 확인해보면 500, 530인 날도 많았다.&amp;nbsp;&lt;br&gt;끌어올린 심폐 능력에 비해 근력이 곧잘 버텨지지 않았다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;지난달 k마라톤 하프 달리면서 복근과 코어에 힘이 빠지면서 상체가 무너지는걸 크게 느꼈기 때문에 이번달은 상체 훈련(?)이라고 하면 거창하지만 대충 푸쉬업, 평행봉, 턱걸이를 섞어서 해줬다.&amp;nbsp;&lt;br&gt;거의 루틴이&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp; &amp;nbsp;퇴근 -&amp;gt;&lt;br&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 옷갈아입고 푸쉬업 + 케틀벨 컬 + ab슬라이드 -&amp;gt;&lt;br&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;한강 이동 -&amp;gt;&lt;br&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 달리는 중간중간에 한스장 들려서 턱걸이와 평행봉&lt;br&gt;&amp;nbsp;&lt;br&gt;이렇게 돌렸다.&lt;br&gt;&amp;nbsp;&lt;br&gt;확실히 상하체가 튼튼해진 것 같긴하다...&lt;br&gt;장거리 달릴때 상체 말리는 문제가 ab슬라이드하면서 많이 좋아졌다.&lt;br&gt;하지만 팔운동을 하면서 팔 근육의 불균형이 심해졌다. 눈바디로 봤을때도 차이가 난다.&lt;br&gt;특히나 달리며 팔치기 할때 오른팔이 훨씬 많이, 크게 움직인다.&lt;br&gt;요걸 어떻게 교정하지 않으면 분명 장거리 달릴 때 문제가 생길 것 같다.&amp;nbsp;&lt;br&gt;우선 11월 제마 풀을 목표로.. 4월에 이어서 마일리지를 가능한 땡겨봐야할 것 같다.&amp;nbsp;&lt;br&gt;짧은 거리보단 긴 거리 위주로... 페이스 신경쓰지 말고........&lt;br&gt;매일 10k 달리기보다 이틀에 20k씩 달리는게 나을 것 같다는 생각이 든다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;다음 대회는 5/30 여명마라톤 하프인데.. 목표는 1시간 23분이다.&amp;nbsp;&lt;br&gt;5월은 연휴도 많고 여명 하프 테이퍼링 하려면 일주일은 또 마일리지를 줄일테니 현실적으로는 아마 250k??? 정도가 가능하지 않을까..&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;아무튼 처음으로 300k를 넘겨봤다....&lt;br&gt;뿌듯하다거나 뭐 그런 생각보다... 도대체 오육백씩 달리는 사람들은 어케하는건지 모르겠다.&amp;nbsp;&lt;br&gt;가능한건가????&amp;nbsp;&lt;br&gt;주 6일 20k씩 달려도 500k가 안되는데????????? 대체 어케함!!!!!&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;아침에 자주자주 달리고 싶은데 일찍 일어나는게 여간 어려운 일이 아니다..&lt;br&gt;수요일 소울루프 아니면 도저히 여섯시에 일어나서 달리러 가지 못하겠다...........&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;2153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsVVho/dJMcag6qreq/cEx5V7w55MenN9V7bqsltk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsVVho/dJMcag6qreq/cEx5V7w55MenN9V7bqsltk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsVVho/dJMcag6qreq/cEx5V7w55MenN9V7bqsltk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsVVho%2FdJMcag6qreq%2FcEx5V7w55MenN9V7bqsltk%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;730&quot; height=&quot;2153&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;2153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>러닝 챌린지</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/271</guid>
      <comments>https://99duuk.tistory.com/271#entry271comment</comments>
      <pubDate>Thu, 30 Apr 2026 17:15:29 +0900</pubDate>
    </item>
    <item>
      <title>2026년 1,2,3월 러닝 챌린지</title>
      <link>https://99duuk.tistory.com/270</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z7QHC/dJMcafTD91W/UyfEPZmhyPO56zKFKZUKh1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z7QHC/dJMcafTD91W/UyfEPZmhyPO56zKFKZUKh1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z7QHC/dJMcafTD91W/UyfEPZmhyPO56zKFKZUKh1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz7QHC%2FdJMcafTD91W%2FUyfEPZmhyPO56zKFKZUKh1%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;1179&quot; height=&quot;1186&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;2월 22일 대구마라톤을 앞두고, 장경인대 부상은 도통 나아질 기미가 보이지 않았다.&lt;br&gt;12월, 1월.. 제대로 훈련했으면 풀마라톤 기대감에 마음이 상당히 부풀었을텐데,&amp;nbsp;&lt;br&gt;이럴거면 미리 취소할 걸 그랬나.. 후회만 가득했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;매일매일 스트레칭하고 주무르고 누르고 쉬어도 차도가 없었다.&lt;br&gt;이번엔 중둔근은 멀쩡했고, 고관절 쪽에 문제가 있다고 생각했다. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGnbaE/dJMcahqow5A/RhSeYA6kcZ2QaFliGM0mu1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGnbaE/dJMcahqow5A/RhSeYA6kcZ2QaFliGM0mu1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGnbaE/dJMcahqow5A/RhSeYA6kcZ2QaFliGM0mu1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGnbaE%2FdJMcahqow5A%2FRhSeYA6kcZ2QaFliGM0mu1%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;410&quot; height=&quot;416&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;대충 눌러봤을때 Tensor Fasciae Latae 부근에 통증이 많았다. 고관절 틀어짐 때문에 해당 부근이 뻣뻣해지거나 뭉치거나.. 염증이 생겼다고 예상했다. &lt;br&gt;하지만 아무리 풀어줘도 낫지를 않았다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;병원&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;결국 1월 28일, 병원에 갔다.&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;김학윤정형외과의원&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;서울 광진구 자양번영로 34&quot; data-og-host=&quot;map.kakao.com&quot; data-og-source-url=&quot;https://map.kakao.com/?itemId=8198123&amp;amp;t_src=share&amp;amp;t_ch=copy_url&amp;amp;t_obj=place&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/gKFVE/dJMb9cBIsFC/AAAAAAAAAAAAAAAAAAAAABR3JDSy_XIbK4DApTq3wUY1a8HvJUxu82M_0GVps8Cz/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=miiTCZTa5FM2A0sGw%2Fi4mVALmRo%3D&quot; data-og-url=&quot;https://map.kakao.com/?itemId=8198123&amp;amp;t_src=share&amp;amp;t_ch=copy_url&amp;amp;t_obj=place&quot;&gt;&lt;a href=&quot;https://map.kakao.com/?itemId=8198123&amp;amp;t_src=share&amp;amp;t_ch=copy_url&amp;amp;t_obj=place&quot; target=&quot;_blank&quot; data-source-url=&quot;https://map.kakao.com/?itemId=8198123&amp;amp;t_src=share&amp;amp;t_ch=copy_url&amp;amp;t_obj=place&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/gKFVE/dJMb9cBIsFC/AAAAAAAAAAAAAAAAAAAAABR3JDSy_XIbK4DApTq3wUY1a8HvJUxu82M_0GVps8Cz/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=miiTCZTa5FM2A0sGw%2Fi4mVALmRo%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;김학윤정형외과의원&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;서울 광진구 자양번영로 34&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;map.kakao.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;아무래도 달리기를 하지 않는 의사들은 대게 달리지 말고 쉬라고 한다. &lt;br&gt;그런데 김학윤 센세는 내가 달리지 못할 것 같은데 달리라고 한다.. (그때는 또 아플까봐 심리적으로 더 그렇게 느껴졌던 것 같다)&lt;br&gt;엑스레이를 찍었고, 체외충격파도 받았다.&lt;br&gt;체외충격파의 효과가 드라마틱하게 느껴지진 않았다. 하지만 병원에 다녀온 덕분에 무엇이 문제인지는 알게됐다.&lt;br&gt;&amp;nbsp;&lt;br&gt;엑스레이를 찍을 때였다. 다리 위치를 잡다말고 선생님이 예고없이 대퇴사두를 매우 강하게 누르셨다.&lt;br&gt;으악!!!!!!! 소리를 지르고 말았다. “&lt;i&gt;음~ 괜찮네~~ 풀마라톤 뛸 수 있어요~~~ 걱정마세요~~&lt;/i&gt;” &lt;br&gt;뭐라했는지 기억은 잘 안나는데 대충 여기저기 뭉친 곳이 많으니 잘 풀어주라고 했다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/URKyr/dJMcafe4WSM/E50sKO4wFSCo2FzRLKsy30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/URKyr/dJMcafe4WSM/E50sKO4wFSCo2FzRLKsy30/img.png&quot; data-alt=&quot;ㅋ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/URKyr/dJMcafe4WSM/E50sKO4wFSCo2FzRLKsy30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FURKyr%2FdJMcafe4WSM%2FE50sKO4wFSCo2FzRLKsy30%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;1194&quot; height=&quot;1455&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1455&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ㅋ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;그리고 집으로 돌아가는 길.. 돌아보니 나는 대퇴사두를 풀어본 적이 없었다..&lt;br&gt;센세가 마자지건을 써보면 좋을거라고 했다. 테라건을 질렀다.&lt;br&gt;대퇴사두를 테라건으로 조졌다. 상당히 아팠다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZZtt2/dJMcahDTVrg/l1xew34vtK9DNEMi4dCV31/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZZtt2/dJMcahDTVrg/l1xew34vtK9DNEMi4dCV31/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZZtt2/dJMcahDTVrg/l1xew34vtK9DNEMi4dCV31/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZZtt2%2FdJMcahDTVrg%2Fl1xew34vtK9DNEMi4dCV31%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;242&quot; height=&quot;242&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;맨 처음엔 선생님이 누르셨던 대퇴사두 윗부분을 조졌다. 한 이틀동안 살짝 건드리면 아플정도로 아팠는데, 이틀 지나니까 괜찮아졌다.&amp;nbsp;&lt;br&gt;대퇴사두가 풀리고 나니까 다음은 봉공근쯤 어디.. 그 다음은 무릎쪽 장경인대 근처 .. 한 대여섯군대 부위가 이틀씩 간격을 두고 풀렸다.&lt;br&gt;테라건은 최고다..&amp;nbsp;거진 2달을 아파했는데,&amp;nbsp;&lt;br&gt;한 2주만에 뛸 수 있는 몸이 되었다.&amp;nbsp;&lt;br&gt;&lt;br&gt;하지만 이 글을 작성하는 현재까지 완전히 장경인대 이슈로부터 멀어지진 않은 것 같다.&amp;nbsp;&lt;br&gt;스트레칭 귀찮아서 빼먹거나 오래 앉아있다가 달리면 슬쩍 그 통증이 올 것 같은 느낌이 들긴 한다......&lt;br&gt;&amp;nbsp;&lt;br&gt;경험적으로 장경인대란게, 무릎부터 고관절까지 (거의 옆구리 근육과 종아리 바깥쪽가지 포함되는 것 같다) 어딘가 특정 부위가 굳거나 뻣뻣해지면, 장경인대가 덩달아 함께 뻣뻣해지고 긴장된다. 그 상태에서 반복적으로 무릎 바깥쪽에 마찰이 생기면 해당 부위에 염증이 생겨버리는 것이다..&lt;br&gt;&amp;nbsp;&lt;br&gt;나의 경우에 결론 내린 셀프진단(?) 장경인대 이슈는 거의 대퇴직근? 저 부위에 원인이 있는 것 같다.&amp;nbsp;&lt;br&gt;다리를 오래 꼬고 앉아있을때 특히 뻣뻣해지는 것 같다.. 내 왼쪽 고관절이 좀 틀어져있는(?) 것도 하나의 원인인 것 같고..&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IJEfY/dJMcaakuWUS/2EuHsrwedwTxncnrkEHBq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IJEfY/dJMcaakuWUS/2EuHsrwedwTxncnrkEHBq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IJEfY/dJMcaakuWUS/2EuHsrwedwTxncnrkEHBq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIJEfY%2FdJMcaakuWUS%2F2EuHsrwedwTxncnrkEHBq1%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;403&quot; height=&quot;355&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;br&gt;&lt;b&gt;대구마라톤 풀&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Etnvg/dJMcahYdMUW/R0aX2xoKfbrjdWvWgfs20K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Etnvg/dJMcahYdMUW/R0aX2xoKfbrjdWvWgfs20K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Etnvg/dJMcahYdMUW/R0aX2xoKfbrjdWvWgfs20K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEtnvg%2FdJMcahYdMUW%2FR0aX2xoKfbrjdWvWgfs20K%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;312&quot; height=&quot;326&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1월 28일에 병원을 다녀왔고 2일부터 다시 달리기 시작했다.&amp;nbsp;&lt;br&gt;저날 5키론가 달렸던 것 같은데 5키로 달리고 숨이 넘어갔다. 몸이 완전히 리셋된 것만 같았다.&lt;br&gt;그리고 일주일동안 대충 10키로 달릴 수 있는 몸으로 끌어올렸다...&lt;br&gt;&amp;nbsp;&lt;br&gt;이떄 대구마라톤 목표는 완주였다. 목표 기록 같은 건 기대도 안하고 그냥 42키로 안걷고 달릴 수만 있음 좋겠다... 라고 생각했다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;원래 계획은 22일 전까지 30키로 한 네다섯번 뛰는 거였는데.. &lt;br&gt;&lt;br&gt;통증이 사라졌긴 했지만 장거리 훈련 했다가 다시 도져버리면 22일에 아예 달리는 걸 포기하게 될 일이 생길까봐..&lt;br&gt;8일부터는 20키로만 목표로 잡고 달렸다.&lt;br&gt;&lt;br&gt;이때 달리기가 정말정말 재미없었다. 억지로 뛰는게 너무 너무 싫었다.&amp;nbsp;&lt;br&gt;지금 더 달리지 않으면 대회날 쪽팔리게 걷게 될거라는 생각으로 두어시간을 참았다.&lt;br&gt;그리고 이때쯤 20k가 달려지는걸 보고 목표를 3시간 40분으로 수정했다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;설까지 술도 먹지 않았다. 이때 확실히 느꼈다. 술을 마시는거랑 달리는 거랑 그닥 상관관계는 없는데 (술을 마셔도 웬만큼 잘 달려진다..)&lt;br&gt;술을 마셔야 유효하게 회복이 된다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;대회날을 복기해보자면...&lt;br&gt;3시간 40분은 무슨 3시간 30분으로 가자~&lt;br&gt;하고서 3시간 30분 이내를 목표로 잡았다.&lt;br&gt;컨디션도 꽤 괜찮았고, 엄마아빠가 아침에 데려다줘서 잠도 나름 충분하게 잤다.&amp;nbsp;&lt;br&gt;20k까지는 꽤나 재밌고 힘들지 않게 달렸다. 요상태 그대로 밀고 가면 320도 가능하겠는데.. 생각했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 26k부터 슬슬 힘빠지기 시작하더니, 330페메를 놓쳐버렸다.&lt;br&gt;30k 부터는 완전히 무너졌다. 그때부터는 한번 멈추면 이제 dnf 아님 10키로를 걸어가야한다....&amp;nbsp;&lt;br&gt;한시간동안 정신줄만 붙잡고 달렸다.&amp;nbsp;&lt;br&gt;이때부턴 페이스가 6분 7분대로 떨어졌던 것 같다..&amp;nbsp;&lt;br&gt;30k 이후로 달리면서 뭔가 깨달은게 있었던 것 같은데 한달지났다고 벌써 다 까먹었다...&lt;br&gt;&amp;nbsp;&lt;br&gt;어쨌든간 완주는 했다.&amp;nbsp;&lt;br&gt;장거리 훈련하지 않은 대가를 확실히 느꼈다...&lt;br&gt;날씨도 너무 더웠고, 후반 코스의 업힐은 욕만 나왔다..&lt;br&gt;30k 이후에 오바 좀 보태서 한 천명이 나를 제쳐 지나간 것 같은데, 한명한명 나를 제쳐나갈 때마다 --오만함 하는 기분이었다....&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGcC7o/dJMcaa5RYtM/hmNbghTzgKqEkOQP9cg6Y0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGcC7o/dJMcaa5RYtM/hmNbghTzgKqEkOQP9cg6Y0/img.jpg&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;1182&quot; style=&quot;width: 28.9982%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGcC7o/dJMcaa5RYtM/hmNbghTzgKqEkOQP9cg6Y0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGcC7o%2FdJMcaa5RYtM%2FhmNbghTzgKqEkOQP9cg6Y0%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;665&quot; height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HFPI5/dJMcaf0n1cy/GCoKDLkrkscawKDzi1LZGK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HFPI5/dJMcaf0n1cy/GCoKDLkrkscawKDzi1LZGK/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 34.3618%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HFPI5/dJMcaf0n1cy/GCoKDLkrkscawKDzi1LZGK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHFPI5%2FdJMcaf0n1cy%2FGCoKDLkrkscawKDzi1LZGK%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7R0Hb/dJMcaiJyGsV/fk9MrWUD3AIKFRdYPNexB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7R0Hb/dJMcaiJyGsV/fk9MrWUD3AIKFRdYPNexB1/img.jpg&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 34.3144%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7R0Hb/dJMcaiJyGsV/fk9MrWUD3AIKFRdYPNexB1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7R0Hb%2FdJMcaiJyGsV%2Ffk9MrWUD3AIKFRdYPNexB1%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;723&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;대구마라톤 이후에 달리는 자세 관련해서 뭔가 깨달은게 있다면..&lt;br&gt;이전에는 지면과 부딪히는 발의 모양이라던가.. 이런거에 집중을 했는데,&amp;nbsp;&lt;br&gt;포어풋이니 미드풋이니 이딴 건 아무래도 상관이 없다고 생각하게 됐다.&lt;br&gt;&amp;nbsp;&lt;br&gt;무슨풋은 걍 힘빼고 달리면 알아서 적당히 맞춰진다.&lt;br&gt;딱 지금 내 수준에서는 엉덩이와 고관절을 제대로 사용하는게 제일 중요하다고 생각든다.&amp;nbsp;&lt;br&gt;작년 여름에는 고관절을 아예 쓰지 않았던 것 같다. 고관절과 둔근 없이 종아리로만 달리다보니 부하가 종아리에만 쌓이고, 그 덕분에 내내 씬스프린트로 고생했던 것 같다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;아직 정확하게 뭐라 설명할만큼 깨닫진 못했는데,&amp;nbsp;&lt;br&gt;아무튼 지면을 딛을때&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erjpHu/dJMcabReqcg/VsgLT1Pvv0YCZuHqZQgVxK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erjpHu/dJMcabReqcg/VsgLT1Pvv0YCZuHqZQgVxK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erjpHu/dJMcabReqcg/VsgLT1Pvv0YCZuHqZQgVxK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FerjpHu%2FdJMcabReqcg%2FVsgLT1Pvv0YCZuHqZQgVxK%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;626&quot; height=&quot;410&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 신경쓰면서 종아리 탄력 잘 활용하고 거따가 리듬만 잘 잡아주면&amp;nbsp;&lt;br&gt;케이던스도 알아서 올라가고 페이스나 자세에 맞는 착지법도 만들어지는 것 같다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;K마라톤 하프&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;작년 MBN에서 달렸던 1시간 26분 기록을 뛰어넘을 수 있을까?&amp;nbsp;&lt;br&gt;아직 작년 11월만큼 몸이 올라오지 않았는데?&amp;nbsp;&lt;br&gt;그래서 3월은 최대한 10k를 40분 전후로 달리는 연습을 많이 했다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;대회가 잡혀있는 주는 보통 테이퍼링? 달리는 거리와 강도를 확확 줄여주는데, 마침 저 주에는 자해런을 하느라 거리와 강도를 줄이는데 실패했다.&amp;nbsp;&lt;br&gt;대회 바로 전날, 28일에 6k 정도 몸풀기로 달리기를 하는데.. 낮은 강도로 짧은 거리를 뛰는데도 상당히 힘들었다.&amp;nbsp;&lt;br&gt;쌓인 데미지를 제대로 못풀었구나.. 내일 대회.. 망했군.. 생각했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;대회장에 도착했더니 광화문 대회치고 사람이 적었다. 나름(?) 쾌적했다. &lt;br&gt;특히 K그룹은 출발선에서 한 열댓명이 팔벌려높이뛰기 해도 남을 정도로 공간이 넉넉했다.&lt;br&gt;&lt;br&gt;목표 기록은 이전 기록인 1시간 26분을 뚫고, 1시간 25분 59초로 잡았다.&lt;br&gt;1시간 25분 페이서만 잘 따라가보기로 했다.&lt;br&gt;내 앞에서 출발한 사람이 한 50명? 도 안됐던 것 같다.&lt;br&gt;사람이 너무 많이 않아서 상당히 쾌적했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;어째저째 달리다보니까 예상했던 기록보다 훨씬 잘 나왔다.&lt;br&gt;대구마라톤에서 한번 페이서를 놓치니까 도저히 따라잡을 수 없다는 걸 크게 느꼈기 때문에...&lt;br&gt;이날은 페이서만 좇아갔다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;18k에서 페이서가&amp;nbsp;&lt;i&gt;치고 가세요!!! 얼른 나가세요!!!&lt;/i&gt; 외쳤다.&amp;nbsp;&lt;br&gt;근데 도저히 치고 나갈 수가 없었다.&amp;nbsp;&lt;br&gt;한 일키로 심기일전하고... 19k에서 치고 나갔다.&lt;br&gt;이미 19k까지 빡런이었는데, ㄹㅇ 이악물고 더 빡런했다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SYXgP/dJMcagSyoZ1/MFXTDwDW6NG6DUNRbkmJl1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SYXgP/dJMcagSyoZ1/MFXTDwDW6NG6DUNRbkmJl1/img.jpg&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;1060&quot; style=&quot;width: 46.0909%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SYXgP/dJMcagSyoZ1/MFXTDwDW6NG6DUNRbkmJl1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSYXgP%2FdJMcagSyoZ1%2FMFXTDwDW6NG6DUNRbkmJl1%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;741&quot; height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr8Gaf/dJMcabqckpL/mtpsKQb8ZCEOYOKtp50Qm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr8Gaf/dJMcabqckpL/mtpsKQb8ZCEOYOKtp50Qm0/img.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;5000&quot; style=&quot;width: 52.7464%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr8Gaf/dJMcabqckpL/mtpsKQb8ZCEOYOKtp50Qm0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr8Gaf%2FdJMcabqckpL%2FmtpsKQb8ZCEOYOKtp50Qm0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;5000&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdybgc/dJMcagSySOI/jtbj55kojTE4D2J3LzKFTK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdybgc/dJMcagSySOI/jtbj55kojTE4D2J3LzKFTK/img.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;5328&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdybgc/dJMcagSySOI/jtbj55kojTE4D2J3LzKFTK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdybgc%2FdJMcagSySOI%2Fjtbj55kojTE4D2J3LzKFTK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;5328&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DWneK/dJMcagyd9ng/Ug0kB8GXpnOtDkwW4PS5bk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DWneK/dJMcagyd9ng/Ug0kB8GXpnOtDkwW4PS5bk/img.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;5328&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DWneK/dJMcagyd9ng/Ug0kB8GXpnOtDkwW4PS5bk/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDWneK%2FdJMcagyd9ng%2FUg0kB8GXpnOtDkwW4PS5bk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;5328&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSWcj0/dJMcagZiOx4/YDG8S4eI4tLSfjaPxq7eb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSWcj0/dJMcagZiOx4/YDG8S4eI4tLSfjaPxq7eb1/img.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;5328&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSWcj0/dJMcagZiOx4/YDG8S4eI4tLSfjaPxq7eb1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSWcj0%2FdJMcagZiOx4%2FYDG8S4eI4tLSfjaPxq7eb1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;5328&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwJ6KH/dJMcaiJyGMp/yQ5ZOvkOZFAxmdSunysDOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwJ6KH/dJMcaiJyGMp/yQ5ZOvkOZFAxmdSunysDOk/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwJ6KH/dJMcaiJyGMp/yQ5ZOvkOZFAxmdSunysDOk/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwJ6KH%2FdJMcaiJyGMp%2FyQ5ZOvkOZFAxmdSunysDOk%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bupe7W/dJMb996XNES/4fnhcj8FE8zRnQEpUjxry0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bupe7W/dJMb996XNES/4fnhcj8FE8zRnQEpUjxry0/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bupe7W/dJMb996XNES/4fnhcj8FE8zRnQEpUjxry0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbupe7W%2FdJMb996XNES%2F4fnhcj8FE8zRnQEpUjxry0%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;간만에 상탈런하지 않았다..&lt;/p&gt;</description>
      <category>러닝 챌린지</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/270</guid>
      <comments>https://99duuk.tistory.com/270#entry270comment</comments>
      <pubDate>Thu, 2 Apr 2026 17:26:15 +0900</pubDate>
    </item>
    <item>
      <title>2025년 러닝챌린지</title>
      <link>https://99duuk.tistory.com/269</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNUj5d/dJMcai3PMzx/8LC6bMkrGkspNMtPWNAq7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNUj5d/dJMcai3PMzx/8LC6bMkrGkspNMtPWNAq7K/img.jpg&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1210&quot; style=&quot;width: 49.3163%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNUj5d/dJMcai3PMzx/8LC6bMkrGkspNMtPWNAq7K/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNUj5d%2FdJMcai3PMzx%2F8LC6bMkrGkspNMtPWNAq7K%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;1179&quot; height=&quot;1210&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpLy42/dJMcaibMgUw/YjqJcq3IiKKBk0ZXXdksQk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpLy42/dJMcaibMgUw/YjqJcq3IiKKBk0ZXXdksQk/img.jpg&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;1205&quot; style=&quot;width: 49.5209%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpLy42/dJMcaibMgUw/YjqJcq3IiKKBk0ZXXdksQk/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpLy42%2FdJMcaibMgUw%2FYjqJcq3IiKKBk0ZXXdksQk%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;1179&quot; height=&quot;1205&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;24년 10월,&lt;br&gt;&lt;a href=&quot;https://99duuk.tistory.com/170&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;2024.11.06 - [러닝 챌린지] - 2024년 10월 러닝 챌린지&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;2024년 10월 러닝 챌린지&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;2024년 현황입니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10월은 간당간당하게 100킬로를 채웠읍니다.&amp;nbsp;&amp;nbsp;러닝붐으로 달리는 사람이 많아져 좋습니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;운동을 하면 반드시 건강해집니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;이 기세를 몰아어제보다 나&quot; data-og-host=&quot;99duuk.tistory.com&quot; data-og-source-url=&quot;https://99duuk.tistory.com/170&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bIovGc/dJMb88689CB/AAAAAAAAAAAAAAAAAAAAAE4GkbLIKJYhvaAYn70CEP30wL99k89qUby_KKSW2LMl/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8nzLeRR%2FwUfeSo7I79NvoEa4%2F9M%3D&quot; data-og-url=&quot;https://99duuk.tistory.com/170&quot;&gt;&lt;a href=&quot;https://99duuk.tistory.com/170&quot; target=&quot;_blank&quot; data-source-url=&quot;https://99duuk.tistory.com/170&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bIovGc/dJMb88689CB/AAAAAAAAAAAAAAAAAAAAAE4GkbLIKJYhvaAYn70CEP30wL99k89qUby_KKSW2LMl/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8nzLeRR%2FwUfeSo7I79NvoEa4%2F9M%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;2024년 10월 러닝 챌린지&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;2024년 현황입니다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10월은 간당간당하게 100킬로를 채웠읍니다.&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;/p&gt;&lt;p class=&quot;og-host&quot;&gt;99duuk.tistory.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;매달 이렇게 기록을 남겨두려 했는데&amp;nbsp;&lt;br&gt;&lt;br&gt;딱 11월부터 곧바로 달리기를 멈췄다.&amp;nbsp;&lt;br&gt;회사일 때문이었는지 아님 걍 런태기였던건지.. 기억도 안난다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;봄&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 25년 3월 3일이던가..&amp;nbsp;&lt;br&gt;어린이대공원에서 다시 달리기를 시작했다. 3k 달리고 죽을 뻔 했던 것 같다..&lt;br&gt;그때부터 슬슬 다시 달리기 시작했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;여름&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 본격적으로 6월부터 180k 정도씩 마일리지를 늘렸다.&amp;nbsp;&lt;br&gt;당시 몸상태로는 급격히 늘렸던 편이라 내내 온갖 하체 부상을 달고 살았다.&amp;nbsp;&lt;br&gt;가장 불편했던 건 씬스프린트였다.&amp;nbsp;이때는 오로지 종아리로만 달렸다.&amp;nbsp;&lt;br&gt;모든 데미지가 종아리에 쌓였다. 일주일에 반은 절뚝거렸다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;가장 더웠던 여름에 가장 재밌게 달렸다.&amp;nbsp;&lt;br&gt;이때는 달리기만 하면 산책하는 개마냥 실실 웃음이 나왔다.&amp;nbsp;&lt;br&gt;달리는게 젤 재밌었던 시기였다. 더 달리고 싶었는데 회복 속도가 쌓이는 데미지를 좇아가질 못했다.&lt;br&gt;억지로 참았던 기억이 난다. 이때는 달리기 때문에 술 생각도 없었다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;가까운 곳에서 대회가 열려서 갈만한 대회는 보이는대로 다 신청했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;가을&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;재밌게 달리다보니 기록이 생각보다 잘 나왔다.&amp;nbsp;&lt;br&gt;한창 오만해지던 참이었다.&amp;nbsp;&lt;br&gt;11월 16일 MBN 하프에서 1시간 30분을 목표로 했는데,&amp;nbsp;&lt;br&gt;뛰고 보니 1시간 26분대였다. 대체 어떻게 한건지도 모르겠는데..&amp;nbsp;&lt;br&gt;암튼 이대로만 달리면 나 조만간 서브3 하겠다. 뒤졌다 세상아.. 거렸다.......&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 장경인대 부상이 찾아왔다.&lt;br&gt;달리기를 처음 시작했던 23년에도 장경인대 덕분에 두어달을 제대로 달리지 못했었다.&lt;br&gt;그때 통증의 원인은 중둔근이었다. 중둔근만 풀어줬더니 몇주만에 다시 달릴 수 있게 됐었다.&lt;br&gt;이번 장경인대도 중둔근 때문일거라고 생각했다. 그래서 중둔근 스트레칭이나 몇주하면 괜찮겠거니 별 걱정을 하지 않았다.&lt;br&gt;10k 달려보니 좀 괜찮아졌군. 내일은 하프 뛰어야지~ 하고서 뛰면 15k에서 장경인대 통증이 도졌고, 지하철을 타고 집에 돌아갔다.&lt;br&gt;&amp;nbsp;&lt;br&gt;좀 괜찮다 싶어서 달릴 때마다 매번 다시 다치니까 그 재밌던 달리기가 쬐끔씩 두려워(?)졌다.&amp;nbsp;&lt;br&gt;달리면=아프게될거다. 파블로프의 개마냥 달릴때마다 또 아플까봐 몹시 쫄아있었다.&amp;nbsp;&lt;br&gt;덕분에 달리는게 점점 재미없어졌다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;크리스마스 기념 25k 달리고 싶었는데 &lt;br&gt;달릴 수가 없어서 바틀링에 앉아 맥주를 마셨던 기억이 난다. &lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/np0IQ/dJMcabX0yqq/kKC5JQSPfeefknrBvol73K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/np0IQ/dJMcabX0yqq/kKC5JQSPfeefknrBvol73K/img.jpg&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;1054&quot; style=&quot;width: 32.8422%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/np0IQ/dJMcabX0yqq/kKC5JQSPfeefknrBvol73K/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnp0IQ%2FdJMcabX0yqq%2FkKC5JQSPfeefknrBvol73K%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;747&quot; height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daO9Ci/dJMcagE2igE/1KuZ2Bahc1D4LUCIBfIjA0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daO9Ci/dJMcagE2igE/1KuZ2Bahc1D4LUCIBfIjA0/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 30.893%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daO9Ci/dJMcagE2igE/1KuZ2Bahc1D4LUCIBfIjA0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaO9Ci%2FdJMcagE2igE%2F1KuZ2Bahc1D4LUCIBfIjA0%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bll5G0/dJMcadnWQcK/awiJYuF4eSSJfB0DP4NSpK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bll5G0/dJMcadnWQcK/awiJYuF4eSSJfB0DP4NSpK/img.jpg&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;1790&quot; style=&quot;width: 33.9392%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bll5G0/dJMcadnWQcK/awiJYuF4eSSJfB0DP4NSpK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbll5G0%2FdJMcadnWQcK%2FawiJYuF4eSSJfB0DP4NSpK%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;1311&quot; height=&quot;1790&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9rLDM/dJMcac3ErUu/Zr6e0wXBMxpUicLFyTVHfk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9rLDM/dJMcac3ErUu/Zr6e0wXBMxpUicLFyTVHfk/img.jpg&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;1050&quot; style=&quot;width: 34.0058%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9rLDM/dJMcac3ErUu/Zr6e0wXBMxpUicLFyTVHfk/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9rLDM%2FdJMcac3ErUu%2FZr6e0wXBMxpUicLFyTVHfk%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;747&quot; height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAQQZm/dJMcabDH00q/BFmnV59U06TKYkVKQgfwLK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAQQZm/dJMcabDH00q/BFmnV59U06TKYkVKQgfwLK/img.jpg&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;500&quot; style=&quot;width: 31.8343%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAQQZm/dJMcabDH00q/BFmnV59U06TKYkVKQgfwLK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAQQZm%2FdJMcabDH00q%2FBFmnV59U06TKYkVKQgfwLK%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;333&quot; height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boYYlB/dJMcaduGzHl/RoI8WM1K3zy7Cb5crmXZ50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boYYlB/dJMcaduGzHl/RoI8WM1K3zy7Cb5crmXZ50/img.jpg&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;500&quot; style=&quot;width: 31.8343%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boYYlB/dJMcaduGzHl/RoI8WM1K3zy7Cb5crmXZ50/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboYYlB%2FdJMcaduGzHl%2FRoI8WM1K3zy7Cb5crmXZ50%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;333&quot; height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGSYE/dJMcaax2rsK/AmOGS9B0VK7gdwkUSCYpIK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGSYE/dJMcaax2rsK/AmOGS9B0VK7gdwkUSCYpIK/img.jpg&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;1050&quot; style=&quot;width: 33.4673%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGSYE/dJMcaax2rsK/AmOGS9B0VK7gdwkUSCYpIK/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGSYE%2FdJMcaax2rsK%2FAmOGS9B0VK7gdwkUSCYpIK%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;747&quot; height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpEwQR/dJMcacWTy1z/1gkCa0TBnWRLNgwgkUfkbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpEwQR/dJMcacWTy1z/1gkCa0TBnWRLNgwgkUfkbK/img.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;1424&quot; style=&quot;width: 33.564%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpEwQR/dJMcacWTy1z/1gkCa0TBnWRLNgwgkUfkbK/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpEwQR%2FdJMcacWTy1z%2F1gkCa0TBnWRLNgwgkUfkbK%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;1016&quot; height=&quot;1424&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceZLKS/dJMcaiCN1WQ/gFgx6Iv9ZJrm9JbrRTjeL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceZLKS/dJMcaiCN1WQ/gFgx6Iv9ZJrm9JbrRTjeL1/img.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;1004&quot; style=&quot;width: 30.6432%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceZLKS/dJMcaiCN1WQ/gFgx6Iv9ZJrm9JbrRTjeL1/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceZLKS%2FdJMcaiCN1WQ%2FgFgx6Iv9ZJrm9JbrRTjeL1%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;654&quot; height=&quot;1004&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DRLb2/dJMcahDTq7g/AuG6gnLhbhFIytmqKrDXv0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DRLb2/dJMcahDTq7g/AuG6gnLhbhFIytmqKrDXv0/img.jpg&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot; style=&quot;width: 35.2836%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DRLb2/dJMcahDTq7g/AuG6gnLhbhFIytmqKrDXv0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRLb2%2FdJMcahDTq7g%2FAuG6gnLhbhFIytmqKrDXv0%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;2160&quot; height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDxcEu/dJMcaax2rtQ/YpYP4BdLJQ1wjbqkm75Fl1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDxcEu/dJMcaax2rtQ/YpYP4BdLJQ1wjbqkm75Fl1/img.jpg&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;1054&quot; style=&quot;width: 31.1745%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDxcEu/dJMcaax2rtQ/YpYP4BdLJQ1wjbqkm75Fl1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDxcEu%2FdJMcaax2rtQ%2FYpYP4BdLJQ1wjbqkm75Fl1%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;745&quot; height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWJ3Db/dJMcaflNVuX/n61vGSQZ8Jk47XY8mYXJO0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWJ3Db/dJMcaflNVuX/n61vGSQZ8Jk47XY8mYXJO0/img.jpg&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;1054&quot; style=&quot;width: 31.2163%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWJ3Db/dJMcaflNVuX/n61vGSQZ8Jk47XY8mYXJO0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWJ3Db%2FdJMcaflNVuX%2Fn61vGSQZ8Jk47XY8mYXJO0%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;746&quot; height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWgJ9s/dJMb99TpwvW/6PBXuQi298GZNqTXQMwUk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWgJ9s/dJMb99TpwvW/6PBXuQi298GZNqTXQMwUk1/img.jpg&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2699&quot; style=&quot;width: 36.6364%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWgJ9s/dJMb99TpwvW/6PBXuQi298GZNqTXQMwUk1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWgJ9s%2FdJMb99TpwvW%2F6PBXuQi298GZNqTXQMwUk1%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;2160&quot; height=&quot;2699&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAcUPg/dJMb990dvWq/BYZrK3HUm36xZCkJ7Cikk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAcUPg/dJMb990dvWq/BYZrK3HUm36xZCkJ7Cikk1/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 30.519%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAcUPg/dJMb990dvWq/BYZrK3HUm36xZCkJ7Cikk1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAcUPg%2FdJMb990dvWq%2FBYZrK3HUm36xZCkJ7Cikk1%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch6F86/dJMcaa5RYrM/oUc0UjBWJgTUXE00SyFal1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch6F86/dJMcaa5RYrM/oUc0UjBWJgTUXE00SyFal1/img.jpg&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;1086&quot; style=&quot;width: 30.519%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch6F86/dJMcaa5RYrM/oUc0UjBWJgTUXE00SyFal1/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch6F86%2FdJMcaa5RYrM%2FoUc0UjBWJgTUXE00SyFal1%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;724&quot; height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;25년의 달리기를 돌아보고 한마디로 요약하자면..&quot;와 진짜 달리기 개꿀잼 !!!!!!!&quot;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;</description>
      <category>러닝 챌린지</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/269</guid>
      <comments>https://99duuk.tistory.com/269#entry269comment</comments>
      <pubDate>Thu, 2 Apr 2026 17:00:46 +0900</pubDate>
    </item>
    <item>
      <title>ZooKeeper 볼륨 꼬임으로 Kafka 죽는 문제</title>
      <link>https://99duuk.tistory.com/268</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.3.0
    container_name: DI-zookeeper
    ports:
      - &quot;2181:2181&quot;
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_SERVER_ID: 1
      ZOOKEEPER_SERVERS: zookeeper:2888:3888
    volumes:
      - ./docker-data/zookeeper:/var/lib/zookeeper
    networks:
      - kafka-network

   kafka:
    image: confluentinc/cp-kafka:7.3.0
    container_name: DI-kafka-1
    restart: unless-stopped
    ports:
      - &quot;9092:9092&quot;
      - &quot;29092:29092&quot;
      - &quot;9999:9999&quot;
    environment:
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:19092,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092
      KAFKA_LISTENERS: INTERNAL://0.0.0.0:19092,EXTERNAL://0.0.0.0:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_ZOOKEEPER_CONNECT: &quot;zookeeper:2181&quot;
      KAFKA_BROKER_ID: 1
      KAFKA_LOG4J_LOGGERS: &quot;kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO&quot;
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_JMX_PORT: 9999
      KAFKA_JMX_HOSTNAME: ${DOCKER_HOST_IP:-127.0.0.1}
      KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: &quot;true&quot;
    depends_on:
      - zookeeper
    volumes:
      - ./docker-data/kafka:/var/lib/kafka/data
    networks:
      - kafka-network&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;up down 할때 한번씩 kafka가 안올라옴.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;up down up 하면 보통 올라오곤 했는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;up down up down up 해도 안올라옴;;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inspect 날려보니&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;docker inspect DI-kafka-1 --format '{{range .Mounts}}{{println .Source &quot;-&amp;gt;&quot; .Destination}}{{end}}'
docker inspect DI-zookeeper --format '{{range .Mounts}}{{println .Source &quot;-&amp;gt;&quot; .Destination}}{{end}}'

/Users/tars/Documents/namu/gitlab/di-backend/docker-data/kafka -&amp;gt; /var/lib/kafka/data
/var/lib/docker/volumes/ba0bab31ff4baa9903e8a5d297b13fbd23ca24108f9aebf656ebe0908af618cc/_data -&amp;gt; /etc/kafka/secrets

/Users/tars/Documents/namu/gitlab/di-backend/docker-data/zookeeper -&amp;gt; /var/lib/zookeeper
/var/lib/docker/volumes/7953da39d7dda53bcefd827dee910750e391a16552f87c0cc662f8165300e8ea/_data -&amp;gt; /etc/zookeeper/secrets
/var/lib/docker/volumes/8ab3a16e5f539536a6cd27e47aee946450778aca269db79808a51c0cb5de2eae/_data -&amp;gt; /var/lib/zookeeper/data
/var/lib/docker/volumes/676ddc837a8a0eb9bf4cbecd83a048a719031d6f2e7ac955589005d97b4eefe9/_data -&amp;gt; /var/lib/zookeeper/log&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka가 죽는 직접 트리거:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ZooKeeper의 cluster.id(새로 생성됨) &amp;ne; Kafka 디스크의 cluster.id(meta.properties에 남아있음)&lt;/li&gt;
&lt;li&gt;그래서 Kafka가 &lt;code&gt;InconsistentClusterIdException&lt;/code&gt; 던지고 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Zookeeper 데이터가 의도한 &lt;code&gt;./docker-data/zookeeper&lt;/code&gt;에 안 쌓이고 있음&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/Users/.../docker-data/zookeeper -&amp;gt; /var/lib/zookeeper&lt;/code&gt; (의도한 bind mount)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/var/lib/docker/volumes/... -&amp;gt; /var/lib/zookeeper/data&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/var/lib/docker/volumes/... -&amp;gt; /var/lib/zookeeper/log&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 안에서 /var/lib/zookeeper는 호스트 폴더로 붙였는데,&lt;br /&gt;그 안의 핵심 디렉토리인 /var/lib/zookeeper/data, /var/lib/zookeeper/log는 &amp;ldquo;익명(anonymous) 볼륨&amp;rdquo;이 따로 덮어씌워져 있음.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;진짜 중요한 ZK 상태(클러스터 ID 포함)는 익명 볼륨에 저장&lt;/b&gt;되고,&lt;br /&gt;컨테이너가 재생성되면 그 익명 볼륨이 새로 생기면서 &lt;b&gt;cluster.id가 바뀔 수 있음&lt;/b&gt; &amp;rarr; Kafka는 기존 &lt;code&gt;meta.properties&lt;/code&gt;를 들고 있어서 &lt;b&gt;ID mismatch&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&gt;컨테이너가 한번 &lt;b&gt;재생성(recreate)&lt;/b&gt; 되었거나 (&lt;code&gt;up -d&lt;/code&gt; 하면서 설정 바뀌어 recreate, 또는 down/up, 이미지 재풀 등)&lt;/li&gt;
&lt;li&gt;그때 ZK의 &lt;b&gt;익명 볼륨이 새로 붙으면서&lt;/b&gt; cluster.id가 바뀐 거라고 보면 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상위 디렉터리를  bind해서 생긴 문제임.&lt;/h3&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;- ./docker-data/zookeeper:/var/lib/zookeeper&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 뜰 때 &lt;b&gt;더 구체적인 마운트(하위 경로)&lt;/b&gt; 가 우선으로 적용돼서:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/var/lib/zookeeper&lt;/code&gt; &amp;rarr; 호스트 폴더에 붙음 ✅&lt;/li&gt;
&lt;li&gt;근데 &lt;code&gt;/var/lib/zookeeper/data&lt;/code&gt; &amp;rarr; &lt;b&gt;익명 볼륨이 덮어씀&lt;/b&gt; ❌&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/lib/zookeeper/log&lt;/code&gt; &amp;rarr; &lt;b&gt;익명 볼륨이 덮어씀&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;ldquo;진짜 중요한 ZK 상태(스냅샷/로그/cluster id)&amp;rdquo;는&lt;br /&gt;기대한 &lt;code&gt;./docker-data/zookeeper/...&lt;/code&gt;에 저장되지 않고,&lt;br /&gt;도커가 만든 익명 볼륨 안으로 들어가버린 거.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과, 컨테이너가 한 번 recreate 되거나(또는 &lt;code&gt;down -v&lt;/code&gt; 같은 거) 익명 볼륨이 바뀌는 순간&lt;br /&gt;ZK의 cluster id가 새로 만들어지고 &amp;rarr; Kafka의 meta.properties랑 mismatch가 터진 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서,&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;- ./docker-data/zookeeper/data:/var/lib/zookeeper/data
- ./docker-data/zookeeper/log:/var/lib/zookeeper/log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아예 하위 경로를 &lt;b&gt;직접&lt;/b&gt; 바인드해버리면 익명 볼륨이 끼어들 틈이 없어짐.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h5&gt;조치 결과&lt;/h5&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;docker inspect DI-kafka-1 --format '{{range .Mounts}}{{println .Source &quot;-&amp;gt;&quot; .Destination}}{{end}}'
docker inspect DI-zookeeper --format '{{range .Mounts}}{{println .Source &quot;-&amp;gt;&quot; .Destination}}{{end}}'

/Users/tars/Documents/namu/gitlab/di-backend/docker-data/kafka -&amp;gt; /var/lib/kafka/data
/var/lib/docker/volumes/d5849ed8d6e705530c7559a38119be9f4c58cb31821165be311fd491043e81dd/_data -&amp;gt; /etc/kafka/secrets

/Users/tars/Documents/namu/gitlab/di-backend/docker-data/zookeeper/data -&amp;gt; /var/lib/zookeeper/data
/Users/tars/Documents/namu/gitlab/di-backend/docker-data/zookeeper/log -&amp;gt; /var/lib/zookeeper/log
/var/lib/docker/volumes/fecffd194e9ae3ee5fd3c0bbbf9179e1c75316e104f79b3910638e1f16611161/_data -&amp;gt; /etc/zookeeper/secrets&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 ZK 핵심 상태(스냅샷/로그/cluster id)가 호스트 폴더에 영속 저장됨. 더 이상 익명 볼륨이 data/log를 덮어쓰지 않음.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kafka</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/268</guid>
      <comments>https://99duuk.tistory.com/268#entry268comment</comments>
      <pubDate>Wed, 7 Jan 2026 10:40:23 +0900</pubDate>
    </item>
    <item>
      <title>장자 길라잡이 76 - 두려워 말라</title>
      <link>https://99duuk.tistory.com/267</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;SE-feeed95e-812b-462e-a99b-7939810de972&quot; data-a11y-title=&quot;본문&quot; data-compid=&quot;SE-feeed95e-812b-462e-a99b-7939810de972&quot;&gt;
&lt;div&gt;
&lt;div data-direction=&quot;top&quot; data-compid=&quot;SE-feeed95e-812b-462e-a99b-7939810de972&quot; data-unitid=&quot;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;SE-3c41f715-8e11-4056-92e5-ac561a0d0cae&quot;&gt;
&lt;p id=&quot;SE-8d79c341-0fae-4fc8-ad9c-7adbefb1ddf3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-0be8567a-8665-46e4-8e5f-c027f2fb9cda&quot; data-a11y-title=&quot;embed 링크&quot; data-compid=&quot;SE-0be8567a-8665-46e4-8e5f-c027f2fb9cda&quot;&gt;
&lt;div&gt;
&lt;div data-direction=&quot;top&quot; data-compid=&quot;SE-0be8567a-8665-46e4-8e5f-c027f2fb9cda&quot; data-unitid=&quot;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=_OPA04ZAuKU&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/btQxHF/hyZQwSo8Cp/nuwZa6SdnR610EjgWHskm1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=726_204_858_348,https://scrap.kakaocdn.net/dn/LdyQ2/hyZQrp10fN/wsmMs8donMmrsR83X3el2K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=726_204_858_348&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[장자 길라잡이] 76 - 인생은 죽기 아니면 까무러치기다&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/_OPA04ZAuKU&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;SE-22ea0570-efec-4361-8324-463b2a626c2f&quot; data-a11y-title=&quot;본문&quot; data-compid=&quot;SE-22ea0570-efec-4361-8324-463b2a626c2f&quot;&gt;
&lt;div&gt;
&lt;div data-direction=&quot;top&quot; data-compid=&quot;SE-22ea0570-efec-4361-8324-463b2a626c2f&quot; data-unitid=&quot;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;SE-fa8f2239-83d9-4916-9dc9-63fdbaf8583f&quot;&gt;
&lt;p id=&quot;SE-17471555-4acf-450d-b77a-438389725864&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내가 받은 관용을 나도 돌려주는 거예요. 누군가한테,&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ed2a7183-4964-4631-851a-cc0bfa2f8bcf&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 방식이 방식이 바람직한 것이었든&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-bfc8e730-b7a1-41f8-8dec-94af89ae7b8b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇지 못한 것이었든&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d1fc6c7e-eea4-449a-8b9f-1130fd73794c&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떻게 한번 나답게 살아보려고 했던 순간들&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-236983d5-ebc4-4043-a205-0b00778a4e36&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 시간들이 내게 주는 가르침이 분명히 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-b8ead15c-342b-48d9-aa32-04a8efdbce66&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그걸 장자철학에서는, 그런 걸 참된 앎이라 그러고, 그런 걸 덕이라 그러는 거에요&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-338a9478-7db3-4d78-9690-04f3443b7e5e&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나답게 사는 게 뭔지를 알아내느라 엎어지고 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;깨졌던 순간들 &lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-9908d1ae-ffba-4041-a119-ddf79b223044&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그로부터 참된 앎과 관용과 포용을 배운다. &lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-4a193f35-71ea-42f3-86c8-d003404f59a4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이건 누가 가르쳐서 배워지는 것도 아니고 &lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-38860988-3c3c-4fdb-8d12-7fd1e007730f&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;돈주고 배울 수 있는 것도 아니고 &lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-b78036c2-48b2-4671-a3e9-e915cce603ec&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무튼 그래요.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ca6d040f-42b8-4e4e-b566-c3d406510ba6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-3287b617-4a04-47c0-a2aa-eecc101d9271&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기타</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/267</guid>
      <comments>https://99duuk.tistory.com/267#entry267comment</comments>
      <pubDate>Tue, 30 Dec 2025 00:15:31 +0900</pubDate>
    </item>
    <item>
      <title>groupingBy, getOrDefalut</title>
      <link>https://99duuk.tistory.com/266</link>
      <description>&lt;p&gt;간만에 자바..&lt;/p&gt;
&lt;h2&gt;1. DB 1회 조회 + 자바단 그루핑 vs 반복문 내부 다중 DB 조회&lt;/h2&gt;
&lt;h3&gt; ️ 패턴 A: 반복문 안에서 DB 여러 번 조회 (N+1 문제)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;의식의 흐름 버전 (많이들 처음에 이렇게 생각함):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;Student&amp;gt; studentList = repository.selectStudentList(); // 학생 전체 조회

for (Student s : studentList) {
    List&amp;lt;Subject&amp;gt; subjects =
        repository.selectSubjectListByStudentId(s.getStudentId()); // 학생별 과목

    s.setSubjectList(subjects);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;selectStudentList()&lt;/code&gt; → 1번&lt;/li&gt;
&lt;li&gt;학생이 100명 → &lt;code&gt;selectSubjectListByStudentId(...)&lt;/code&gt; 100번&lt;/li&gt;
&lt;li&gt;합계: &lt;strong&gt;DB 호출 101번 (N+1 패턴)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DB round-trip이 너무 많다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DB는 네트워크 + 커넥션 풀 + 쿼리 파싱 등 오버헤드가 큼.&lt;/li&gt;
&lt;li&gt;1번 vs 100번은 차이가 어마어마함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;성능이 데이터 개수에 비례해 폭증&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;학생 수 n → SQL 호출 횟수 = n+1&lt;/li&gt;
&lt;li&gt;n이 커질수록 “선형이 아니라 체감상 폭발”  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;트랜잭션/락 유지 시간이 길어질 수 있음&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;한 서비스 메서드 안에서 트랜잭션 걸고 이걸로 반복문 돌리면&lt;/li&gt;
&lt;li&gt;그 동안 DB 락이나 row-level lock이 길어질 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;코드도 보기 애매&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“학생 조회” + “과목 조회” 책임이 섞여 있음.&lt;/li&gt;
&lt;li&gt;서비스 단에서 구조 설계가 지저분해짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt; ️ 패턴 B: DB에서 한 번에 전부 가져와서 자바단에서 그루핑&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;Student&amp;gt; studentList = repository.selectStudentList(); // 학생 전체 1쿼리
List&amp;lt;Subject&amp;gt; subjectList = repository.selectSubjectList(); // 과목 전체 1쿼리

Map&amp;lt;Integer, List&amp;lt;Subject&amp;gt;&amp;gt; subjectGrouped =
    subjectList.stream()
               .collect(groupingBy(Subject::getStudentId));

for (Student s : studentList) {
    int id = s.getStudentId();
    s.setSubjectList(subjectGrouped.getOrDefault(id, new ArrayList&amp;lt;&amp;gt;()));
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;DB 호출: 딱 &lt;strong&gt;2번&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;학생 전체 1번&lt;/li&gt;
&lt;li&gt;과목 전체 1번&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DB 호출 수 고정&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;학생이 10명이어도, 1000명이어도 → &lt;strong&gt;쿼리 2개&lt;/strong&gt;로 끝.&lt;/li&gt;
&lt;li&gt;스케일 올라가도 DB에 부담 적음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;네트워크 왕복 감소&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쿼리 1번 vs 100번은 네트워크 레벨에서 이미 승부 끝.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서비스 계층 책임이 명확&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“데이터 다 가져와서 자바에서 조립해!” → 역할이 단순/명확.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;정책 바꿀 때 자바 코드로 유연하게 수정 가능&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“학생 1명당 과목 수 제한”, “정렬 순서”, “필터 로직” 등을&lt;/li&gt;
&lt;li&gt;SQL이 아니라 자바에서 바꿀 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;2. groupingBy 내부 동작 요약 (조금 더 깊게)&lt;/h2&gt;
&lt;h3&gt;사용 코드 (학생/과목 예제)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Map&amp;lt;Integer, List&amp;lt;Subject&amp;gt;&amp;gt; subjectGrouped =
    subjectList.stream()
               .collect(groupingBy(Subject::getStudentId));&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;groupingBy의 시그니처 (간단)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static &amp;lt;T, K&amp;gt; Collector&amp;lt;T, ?, Map&amp;lt;K, List&amp;lt;T&amp;gt;&amp;gt;&amp;gt;
groupingBy(Function&amp;lt;? super T, ? extends K&amp;gt; classifier)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;T&lt;/code&gt; : 스트림의 각 요소 타입 → 여기서는 &lt;code&gt;Subject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;K&lt;/code&gt; : 그룹핑에 사용할 key 타입 → 여기서는 &lt;code&gt;Integer&lt;/code&gt; (studentId)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;classifier&lt;/code&gt; : &lt;code&gt;T → K&lt;/code&gt; 함수, 여기서는 &lt;code&gt;Subject::getStudentId&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;내부적으로 하는 일 (개념)&lt;/h4&gt;
&lt;p&gt;subjectList = &lt;code&gt;[s1, s2, s3, ...]&lt;/code&gt; 를 순회하면서:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;key = classifier.apply(subject)&lt;/code&gt; → &lt;code&gt;subject.getStudentId()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;map&lt;/code&gt;에서 &lt;code&gt;key&lt;/code&gt;에 해당하는 리스트를 찾음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;없으면 &lt;code&gt;new ArrayList&amp;lt;Subject&amp;gt;()&lt;/code&gt; 만들어서 map에 넣음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그 리스트에 현재 subject를 &lt;code&gt;add&lt;/code&gt; 해서 집어넣음    &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;예제: subjectList가 이렇게 생겼다 치자&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Subject(studentId=1, name=&amp;quot;수학&amp;quot;)
Subject(studentId=1, name=&amp;quot;영어&amp;quot;)
Subject(studentId=2, name=&amp;quot;국어&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;groupingBy 후 map:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;{
  1 -&amp;gt; [ (1,&amp;quot;수학&amp;quot;), (1,&amp;quot;영어&amp;quot;) ],
  2 -&amp;gt; [ (2,&amp;quot;국어&amp;quot;) ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;flow를 진짜 한 줄씩 따라가면:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;첫 번째 subject (1, &amp;quot;수학&amp;quot;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key = 1&lt;/li&gt;
&lt;li&gt;map 에 1 없음 → new ArrayList 생성해서 put&lt;/li&gt;
&lt;li&gt;그 리스트에 “수학” 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;두 번째 subject (1, &amp;quot;영어&amp;quot;)    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key = 1&lt;/li&gt;
&lt;li&gt;map 에 이미 key 1 있음 → 기존 리스트에 “영어” 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;세 번째 subject (2, &amp;quot;국어&amp;quot;)    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key = 2&lt;/li&gt;
&lt;li&gt;없으니 새 리스트 만들고 “국어” 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. 왜 타입이 Map&amp;lt;Integer, List&amp;lt;...&amp;gt;&amp;gt; 인가?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;직관 체크&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&amp;quot;그룹핑&amp;quot;이란 “key 하나에 요소 여러 개 붙이는 것”이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;학생 1명은 과목 여러 개 가질 수 있음 → 1:N&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그래서    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Map&amp;lt;Integer, Subject&amp;gt;&lt;/code&gt; ❌ (1:1)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Map&amp;lt;Integer, List&amp;lt;Subject&amp;gt;&amp;gt;&lt;/code&gt; ✅ (1:N)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;groupingBy&lt;/code&gt;는 원래부터 &lt;strong&gt;“key별 List”를 만드는 Collector&lt;/strong&gt; 라서&lt;br&gt;리턴 타입이 항상 &lt;code&gt;Map&amp;lt;K, List&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; 모양으로 나온다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. getOrDefault 전/후 &amp;amp; 객체 상태 차이&lt;/h2&gt;
&lt;h3&gt;코드&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (Student s : studentList) {
    int id = s.getStudentId();
    List&amp;lt;Subject&amp;gt; subjects = subjectGrouped.getOrDefault(id, new ArrayList&amp;lt;&amp;gt;());
    s.setSubjectList(subjects);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Map.get() 만 썼을 때 문제&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;Subject&amp;gt; subjects = subjectGrouped.get(id); // 없으면 null
for (Subject sub : subjects) { ... } // NPE 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;특정 학생이 과목이 하나도 없는 경우, map에는 key가 아예 없을 수 있음 → &lt;code&gt;null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;for (Subject sub : subjects)&lt;/code&gt; 에서 바로 &lt;strong&gt;NullPointerException&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;getOrDefault 의 역할&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;V getOrDefault(Object key, V defaultValue)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;key에 값이 있으면 → 그 값을 리턴&lt;/li&gt;
&lt;li&gt;없으면 → 두 번째 파라미터인 &lt;code&gt;defaultValue&lt;/code&gt; 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그래서:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;subjectGrouped.getOrDefault(id, new ArrayList&amp;lt;&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;은 의미상:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;Subject&amp;gt; tmp = subjectGrouped.get(id);
if (tmp == null) {
    tmp = new ArrayList&amp;lt;&amp;gt;();
}
return tmp;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ 항상 &lt;strong&gt;“null이 아닌 List”&lt;/strong&gt; 를 받게 됨.&lt;/p&gt;
&lt;h3&gt;전/후 비교 (학생 하나 기준)&lt;/h3&gt;
&lt;h4&gt;  before 매핑 / getOrDefault 사용 전&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Student&lt;/code&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;studentId = 10&lt;/code&gt;\&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subjectList = null&lt;/code&gt; (초기 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;map에는:    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key 10이 없을 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  after grouping + getOrDefault + setSubjectList&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Student&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;studentId = 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subjectList = [...]&lt;/code&gt; 또는 &lt;code&gt;[]&lt;/code&gt;(empty list)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“null” 대신 “빈 리스트”를 쓰므로&lt;/li&gt;
&lt;li&gt;이후 로직에서 항상 &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;stream()&lt;/code&gt; 등의 연산을 &lt;strong&gt;NPE 걱정 없이&lt;/strong&gt; 호출 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. 최초 객체 vs 최종 객체 구조 비교&lt;/h2&gt;
&lt;h3&gt;5-1. 최초 상태 (DB에서 바로 가져왔을 때)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;Student&amp;gt; studentList = repository.selectStudentList(); // 학생 전체
List&amp;lt;Subject&amp;gt; subjectList = repository.selectSubjectList(); // 과목 전체&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;studentList&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Student 1 (subjectList = null)&lt;/li&gt;
&lt;li&gt;Student 2 (subjectList = null)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subjectList&lt;/code&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1, &amp;quot;수학&amp;quot;)&lt;/li&gt;
&lt;li&gt;(1, &amp;quot;영어&amp;quot;)&lt;/li&gt;
&lt;li&gt;(2, &amp;quot;국어&amp;quot;)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 시점엔 학생과 과목이 &lt;strong&gt;별개 컬렉션&lt;/strong&gt;이고, 연결 정보는 오직 “키값(studentId)” 로만 존재.    &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;5-2. groupingBy 실행 후&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Map&amp;lt;Integer, List&amp;lt;Subject&amp;gt;&amp;gt; subjectGrouped =
    subjectList.stream()
               .collect(groupingBy(Subject::getStudentId));&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;새로 생긴 객체&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;subjectGrouped&lt;/code&gt; (맵)&lt;ul&gt;
&lt;li&gt;key: studentId (Integer)&lt;/li&gt;
&lt;li&gt;value: 그 studentId를 가진 Subject 리스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;studentList&lt;/code&gt; 자체는 아직 변화 없음 (subjectList = null 그대로)    &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;5-3. 학생 객체에 다시 주입 후 (최종 상태)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (Student s : studentList) {
    int id = s.getStudentId();
    List&amp;lt;Subject&amp;gt; subjects = subjectGrouped.getOrDefault(id, new ArrayList&amp;lt;&amp;gt;());
    s.setSubjectList(subjects);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;studentList&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Student 1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;subjectList = [ (1,&amp;quot;수학&amp;quot;), (1,&amp;quot;영어&amp;quot;) ]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Student 2        &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;subjectList = [ (2,&amp;quot;국어&amp;quot;) ]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Student 3        &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;subjectList = [] (빈 리스트)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subjectGrouped&lt;/code&gt; 맵은 “중간 조립용”으로 사용했지만,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;필요하다면 이후 로직에서도 그대로 재활용 가능(예: 빠른 look-up 이 필요할 때)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  한 줄 요약&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;처음엔 “평평한(flat) 리스트 2개(학생, 과목) + key 기반 연관만 존재”&lt;/li&gt;
&lt;li&gt;마지막엔 “학생 객체 안에 &lt;strong&gt;이미 과목 리스트까지 붙어있는 계층 구조&lt;/strong&gt; 로 변환된 상태”&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;7. 한 방에 다시 정리하면&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DB 한 번 + 자바 grouping&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;N+1 피하고, DB 부하 줄이고, 스케일 좋아짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;groupingBy&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; → &lt;code&gt;Map&amp;lt;K, List&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; 로 재구성하는 도구.&lt;/li&gt;
&lt;li&gt;key는 classifier 함수의 반환값, value는 그 key에 해당하는 요소들의 리스트.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;getOrDefault&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;map.get(key)&lt;/code&gt; 가 null일지 신경 안 쓰고&lt;/li&gt;
&lt;li&gt;“항상 null이 아닌 컬렉션”을 받을 수 있게 해줌 → NPE 방지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;전후 객체 상태 차이&lt;/strong&gt;    &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before: 평평한 리스트 + key 숫자만 존재&lt;/li&gt;
&lt;li&gt;After: 상위 객체(Entrant/Student) 안에 하위 리스트(HostBizar/Subject)가 붙은 트리 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1&gt;✅ 1. groupingBy 의 성능&lt;/h1&gt;
&lt;h3&gt;결론: &lt;strong&gt;O(n)&lt;/strong&gt; → 매우 빠르고 안정적&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;groupingBy&lt;/code&gt;는 내부적으로:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;스트림의 각 요소를 한 번씩만 순회&lt;/li&gt;
&lt;li&gt;key 계산&lt;/li&gt;
&lt;li&gt;HashMap.put() or computeIfAbsent()&lt;/li&gt;
&lt;li&gt;List.add()&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 4단계를 수행한다.&lt;/p&gt;
&lt;h3&gt;시간 복잡도&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;스트림 순회: n번&lt;/li&gt;
&lt;li&gt;HashMap 접근: 평균 O(1)    &lt;/li&gt;
&lt;li&gt;ArrayList add: 평균 O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;➡ &lt;strong&gt;전체 시간: O(n)&lt;/strong&gt;&lt;br&gt;➡ n = 1000건이면 1000번 연산하고 끝&lt;/p&gt;
&lt;h3&gt;메모리 사용량&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Map에 key 개수만큼 엔트리가 생김&lt;/li&gt;
&lt;li&gt;각 value는 &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;결국 subjectList(전체 row)와 메모리 크기가 동일한 정도&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ &lt;strong&gt;당연한 수준, 과도한 메모리 사용 없음.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Java가 groupingBy 에서 실제 수행하는 일&lt;/h3&gt;
&lt;p&gt;아주 단순한 해시 분류 작업이라:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (T element : list) {
    K key = classifier.apply(element);
    map.computeIfAbsent(key, k -&amp;gt; new ArrayList&amp;lt;&amp;gt;()).add(element);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ 우리가 손으로 Map에 목록 채우는 작업과 &lt;em&gt;정확히 동일&lt;/em&gt;&lt;br&gt;→ “DSL처럼 보일 뿐, 실제로는 저수준 단순 반복문”&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;  2. 반복문 + 다중 DB 조회 방식의 성능 (N+1 문제)&lt;/h1&gt;
&lt;p&gt;이건 groupingBy 랑 비교하면 &lt;strong&gt;성능 차이가 수백~수천배 이상&lt;/strong&gt; 날 수도 있다.&lt;/p&gt;
&lt;p&gt;예시:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for each entrant E:
    select * from ZONE_AUTH_HOST_BIZAR where entrant_seq = E.seq&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;출입자 50명, 엔트리 300개면:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DB round-trip 50번&lt;/li&gt;
&lt;li&gt;DB I/O 50번&lt;/li&gt;
&lt;li&gt;DB 쿼리 파싱/플랜 50번&lt;/li&gt;
&lt;li&gt;네트워크 왕복 50번&lt;/li&gt;
&lt;li&gt;트랜잭션 홀딩 시간 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;➡ 성능 관점에서는 groupingBy 와 비교 대상 자체가 아님.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;⚙ getOrDefault 의 성능&lt;/h1&gt;
&lt;h3&gt;결론: 무시해도 될 정도로 가벼움 √&lt;/h3&gt;
&lt;p&gt;내부 구현은:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;V v = map.get(key);
return (v != null || map.containsKey(key)) ? v : defaultValue;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;map.get(key)&lt;/code&gt; → HashMap 접근: O(1)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;containsKey(key)&lt;/code&gt; → HashMap 다시 접근: O(1)&lt;/li&gt;
&lt;li&gt;조건문 → O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;➡ 즉 &lt;strong&gt;O(1)&lt;/strong&gt; 상수 시간.&lt;/p&gt;
&lt;p&gt;1000번 호출해도 몇 마이크로초 수준.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;⭐ 성능 비교 한눈에표&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;시간 복잡도&lt;/th&gt;
&lt;th&gt;DB I/O&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;groupingBy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;CPU 기반 작업, 매우 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;getOrDefault&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;null-safe lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;반복문 + DB조회(N+1)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) + DB n회&lt;/td&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;네트워크·DB 비용 극심&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;➡ &lt;strong&gt;실제 속도 비교 감각&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방법&lt;/th&gt;
&lt;th&gt;엔트리 500개 처리 시간&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;groupingBy&lt;/td&gt;
&lt;td&gt;0.1 ~ 0.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;getOrDefault&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.01 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 DB 쿼리&lt;/td&gt;
&lt;td&gt;100~500 ms (심하면 수초)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;즉 groupingBy 의 오버헤드는 &lt;strong&gt;bytes 단위&lt;/strong&gt;,&lt;br&gt;N+1 의 오버헤드는 &lt;strong&gt;milliseconds ~ seconds 단위&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;  최종 결론&lt;/h1&gt;
&lt;h3&gt;groupingBy / getOrDefault 성능 걱정은 100% 불필요&lt;/h3&gt;
&lt;p&gt;(자바 컬렉션 수준의 연산은 CPU가 밥 먹듯이 처리함)&lt;/p&gt;
&lt;h3&gt;오히려 성능의 핵심은 “DB 호출 횟수”&lt;/h3&gt;
&lt;p&gt;  이걸 최소화하는 것이 전체 성능을 결정함&lt;br&gt;  그래서 groupingBy / toMap / reduce 등 자바 컬렉션 기반 재조립이 정답임&lt;/p&gt;
&lt;hr style=&quot;border: 3px solid black;&quot;&gt;

&lt;h1&gt;만약 학생이 100만명쯤 된다면?&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 1) DB 전체 조회 (단 1회씩)
List&amp;lt;Student&amp;gt; studentList = repository.selectStudentList();
List&amp;lt;Subject&amp;gt; allSubjects = repository.selectAllSubjectList();  
// subjectList: size = 전체 과목 수 (학생ID 포함됨)

// 2) 학생ID 별로 과목들을 병렬 grouping
Map&amp;lt;Long, List&amp;lt;Subject&amp;gt;&amp;gt; subjectGrouped =
    allSubjects.parallelStream()
               .collect(Collectors.groupingByConcurrent(Subject::getStudentId));

// 3) 매핑
studentList.parallelStream().forEach(student -&amp;gt; {
    List&amp;lt;Subject&amp;gt; subjects =
        subjectGrouped.getOrDefault(student.getStudentId(), new ArrayList&amp;lt;&amp;gt;());
    student.setSubjectList(subjects);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;으로 &lt;code&gt;groupingBy 병렬 스트림 사용&lt;/code&gt;을 고려해볼 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;✅ 1. 병렬 스트림(parallelStream) → 언제 쓰면 되는가?&lt;/h1&gt;
&lt;h3&gt;✔ &lt;strong&gt;CPU 연산이 많고, 데이터가 크고, 각 요소가 독립적인 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;예:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100만 학생 목록을 그룹핑/집계&lt;/li&gt;
&lt;li&gt;100만 로그 라인을 파싱·정규화&lt;/li&gt;
&lt;li&gt;AI inference 결과 대량 transform&lt;/li&gt;
&lt;li&gt;정렬, 해싱, 매핑 등 CPU heavy 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;즉, CPU 바운드 + 대량 데이터 처리 + 요소 간 독립적이면 OK&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;✔ &lt;strong&gt;멀티스레드로 동시에 처리해도 안전한 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;공유된 mutable 객체 없음&lt;/li&gt;
&lt;li&gt;부작용(side effect) 없음&lt;/li&gt;
&lt;li&gt;공용 자료구조는 Concurrent 계열 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Map&amp;lt;Long, List&amp;lt;Subject&amp;gt;&amp;gt; grouped =
    subjects.parallelStream()
            .collect(Collectors.groupingByConcurrent(Subject::getStudentId));&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;✔ &lt;strong&gt;성능 이득이 충분할 때&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;parallelStream 은 기본적으로 CPU 코어 수 만큼 쓰레드 생성&lt;br&gt;(8코어면 8개 스레드)&lt;br&gt;입력 데이터가 적으면 overhead(스레드 분배 비용)가 더 커질 수도 있음.&lt;br&gt;&lt;strong&gt;보통 1만~5만 건 이상일 때 이득이 발생&lt;/strong&gt;&lt;br&gt;100만 건이면 병렬이 엄청 도움됨.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;⛔ 2. 병렬 스트림 &lt;strong&gt;절대 지양해야 하는 경우&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;이건 매우 중요함.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;❌ &lt;strong&gt;I/O 바운드(DB, 네트워크, 파일) 작업이 있는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;병렬 스트림은 CPU 멀티스레드 기반이며,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DB 연결은 1개 커넥션 → 다중 스레드가 동시에 못씀&lt;/li&gt;
&lt;li&gt;JDBC 는 스레드 언세이프&lt;/li&gt;
&lt;li&gt;네트워크 작업은 오히려 느려짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;즉:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;병렬 스트림 안에서 DB 조회하면 100% 잘못된 구조&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;예 (절대 금지):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;studentList.parallelStream().forEach(student -&amp;gt; {
    List&amp;lt;Subject&amp;gt; subjects = repository.selectByStudent(student.getId()); // ❌
    student.setSubjects(subjects);
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;❌ &lt;strong&gt;상태를 공유하는 mutable 객체 수정이 있는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;아래처럼 &lt;code&gt;ArrayList&lt;/code&gt;, &lt;code&gt;HashMap&lt;/code&gt; 수정은 병렬에서는 터짐.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;String&amp;gt; output = new ArrayList&amp;lt;&amp;gt;();

items.parallelStream().forEach(i -&amp;gt; output.add(i)); // ❌ concurrency 문제&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;❌ &lt;strong&gt;순서가 중요한 로직&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;parallelStream 은 처리 순서가 보장되지 않음. (처리 결과 순서 x)&lt;/p&gt;
&lt;p&gt;예:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;studentList.parallelStream().forEach(System.out::println); // 처리 순서 뒤죽박죽&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;stream()은 입력 자료구조가 순서를 가지고 있다면(stream() 원소 순서 = 원본 순서)&lt;/p&gt;
&lt;p&gt;✔ groupingBy()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일반 stream: 순서 그대로 accumulation    &lt;/li&gt;
&lt;li&gt;parallelStream + groupingBy(): 내부 순서 보장 안됨 (map 도 경쟁하며 쓰니까)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✔ groupingByConcurrent()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;병렬 grouping 전용&lt;/li&gt;
&lt;li&gt;map 자체가 순서 없는 ConcurrentHashMap 기반&lt;br&gt;  → 순서는 처음부터 기대하는 것이 아님&lt;br&gt;  → &lt;strong&gt;키의 순서도 보장 안됨&lt;/strong&gt;&lt;br&gt;  → &lt;strong&gt;키별 value 리스트의 순서도 보장 안됨&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h3&gt;❌ &lt;strong&gt;키가 적고 그룹이 매우 적은 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;subjectGrouped 의 key 가 몇 개 안 되면&lt;br&gt;concurrent map 락이 성능을 오히려 깎아먹음.&lt;/p&gt;
&lt;p&gt;예:&lt;br&gt;100만개가 &amp;quot;A&amp;quot;, &amp;quot;B&amp;quot;, &amp;quot;C&amp;quot; 로만 그룹핑된다면?&lt;br&gt;병렬로 해봤자 3개 키에서 더럽게 경쟁함 → 오히려 느려짐.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;❌ &lt;strong&gt;서버 환경이 제한된 경우 (CPU가 적음)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;AWS t2.micro 같은 1 vCPU 서버에서는 parallelStream 의미 없음.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;⭐ 병렬 스트림이 “필수”가 되는 경우 예시&lt;/h1&gt;
&lt;h3&gt;예시: 학생 100만 명 + 과목 300만 개 grouping&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Map&amp;lt;Long, List&amp;lt;Subject&amp;gt;&amp;gt; subjectGrouped =
    allSubjects.parallelStream()
               .collect(Collectors.groupingByConcurrent(Subject::getStudentId));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 경우 병렬 효과가 폭발적으로 나타남.&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;  최종 결론&lt;/h1&gt;
&lt;h2&gt;✔ 병렬 스트림을 써도 되는 경우&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터가 &lt;strong&gt;1~10만 이상&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;CPU 연산 중심&lt;/li&gt;
&lt;li&gt;요소 간 완전 독립&lt;/li&gt;
&lt;li&gt;공유 상태 없음&lt;/li&gt;
&lt;li&gt;groupingByConcurrent 같은 thread-safe collector 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;❌ 병렬 스트림 쓰면 안 되는 경우&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터 적음 (수백~수천)&lt;/li&gt;
&lt;li&gt;DB / 네트워크 호출 포함&lt;/li&gt;
&lt;li&gt;순서 중요함&lt;/li&gt;
&lt;li&gt;동일 키 경쟁 심함&lt;/li&gt;
&lt;li&gt;서버 CPU 낮음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>GPT Archiving</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/266</guid>
      <comments>https://99duuk.tistory.com/266#entry266comment</comments>
      <pubDate>Wed, 3 Dec 2025 11:03:08 +0900</pubDate>
    </item>
    <item>
      <title>FLUSH</title>
      <link>https://99duuk.tistory.com/265</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 질문이에요.&lt;br /&gt;flush는 여러 기술 스택에서 &lt;b&gt;공통적으로 등장하지만, 내부적으로는 다소 다른 의미와 역할&lt;/b&gt;을 가지는 개념입니다.&lt;br /&gt;그래서 이 개념을 &lt;b&gt;&quot;원리와 어원부터 차근히&quot;&lt;/b&gt; 이해해두면 여러 기술을 다룰 때 훨씬 유연해져요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 1. 어원: &lt;b&gt;flush&lt;/b&gt;라는 단어의 본래 의미&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어 **&quot;flush&quot;**는 원래 다음과 같은 뜻을 가집니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(액체 등을) 쏟아내다, 씻어내다&lt;/td&gt;
&lt;td&gt;ex) &quot;flush the toilet&quot; (물을 내리다)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&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;rarr; 즉, &lt;b&gt;&quot;어딘가에 임시로 쌓인 걸 실제 대상으로 흘려보내거나 비우는 것&quot;&lt;/b&gt;&lt;br /&gt;&amp;rarr; 소프트웨어에선 주로 **&quot;버퍼에 쌓인 데이터를 대상(디스크, DB 등)에 반영하는 작업&quot;**을 의미함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 2. 일반적인 의미 (공통 개념)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 flush의 공통점:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;지연되어 있거나 메모리에 머물러 있는 상태를, 영속화된 저장소로 강제로 밀어넣는 것&quot;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시에 쌓인 데이터를 디스크로 쓰기&lt;/li&gt;
&lt;li&gt;영속성 컨텍스트에 있는 객체를 DB로 쓰기&lt;/li&gt;
&lt;li&gt;파일 출력 버퍼를 디스크에 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 3. 각 기술에서의 flush 의미 및 차이&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  A. &lt;b&gt;JPA / Hibernate (영속성 컨텍스트 flush)&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;entityManager.flush();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;의미:&lt;/b&gt; 영속성 컨텍스트(1차 캐시)에 있는 변경 내용을 **DB에 반영(쿼리 실행)**함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단, 트랜잭션은 그대로 유지됨&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예: persist() 호출했지만 flush() 전에는 실제 INSERT 쿼리 안 나감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;flush() = SQL 실행 (INSERT/UPDATE/DELETE) &lt;b&gt;하지만 commit 아님&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;flush() 없이는 트랜잭션 내에서 DB에 변화가 안 보일 수 있음&lt;/li&gt;
&lt;li&gt;트랜잭션 커밋 시 자동으로 flush() 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  B. &lt;b&gt;Redis&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;FLUSHDB      # 현재 선택된 DB만 비움
FLUSHALL     # 모든 DB 비움
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의미: &lt;b&gt;Redis의 in-memory 데이터 전체 삭제&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;저장소에서 flush는 보통 &amp;rarr; &lt;b&gt;버퍼 비움 or 캐시 날림&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Redis에선 거의 &lt;b&gt;초기화 명령어에 가까움&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  C. &lt;b&gt;MongoDB&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MongoDB 자체에서 flush라는 명령어는 &lt;b&gt;공식적으로는 없음&lt;/b&gt;,&lt;/li&gt;
&lt;li&gt;하지만 내부적으로 &lt;b&gt;journal flush, cache flush&lt;/b&gt; 등의 용어로 쓰임&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;db.runCommand({fsync: 1})
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의미: 메모리 &amp;rarr; 디스크로 &lt;b&gt;flush&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예: WAL(journal)을 디스크에 강제 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  D. &lt;b&gt;RDBMS (예: Oracle, MySQL 등)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL에서는 일반적으로 flush라는 명령은 없지만, MySQL에는 존재합니다:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;FLUSH TABLES;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의미:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FLUSH TABLES: 열려 있는 테이블 캐시를 디스크로 반영하고 닫음&lt;/li&gt;
&lt;li&gt;FLUSH PRIVILEGES: GRANT/REVOKE 이후 권한 캐시를 다시 로드함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  E. &lt;b&gt;파일 스트림 / 출력 버퍼 (Java, C, Python 등)&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;writer.flush();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의미: &lt;b&gt;버퍼에 임시로 저장된 데이터를 파일(디스크)에 강제로 쓰기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예: PrintWriter, BufferedOutputStream 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;flush 안 하면 프로그램 종료 시 데이터 유실 가능&lt;/li&gt;
&lt;li&gt;autoFlush=true 설정하면 매번 flush함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 4. flush vs commit vs save 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념 의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;flush()&lt;/td&gt;
&lt;td&gt;변경사항을 &lt;b&gt;메모리 &amp;rarr; 저장소로 밀어넣음&lt;/b&gt;, 하지만 트랜잭션 종료 아님&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit()&lt;/td&gt;
&lt;td&gt;트랜잭션을 종료하며 DB에 &lt;b&gt;최종 반영&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;save()&lt;/td&gt;
&lt;td&gt;(JPA 기준) 객체를 영속성 컨텍스트에 등록, flush 없이 SQL 실행되지 않을 수도 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 5. 캐시 시스템의 flush&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분 캐시 서버(예: Redis, Memcached, Spring Cache)에서는 flush = &lt;b&gt;캐시 삭제&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;ex) Redis FLUSHALL, Spring Cache clear()&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 6. flush가 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황 이유&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JPA&lt;/td&gt;
&lt;td&gt;쿼리 강제 실행 시점 제어, 테스트 시에도 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파일 I/O&lt;/td&gt;
&lt;td&gt;프로그램 종료 전에 버퍼를 비워야 데이터 유실 방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB 운영&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;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 7. flush와 유사하거나 헷갈리는 개념&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;clear() &amp;rarr; 메모리 버퍼/컨텍스트를 비우는 것 (JPA, Java 등)&lt;/li&gt;
&lt;li&gt;sync() &amp;rarr; 디스크 동기화 (fsync, sync 등)&lt;/li&gt;
&lt;li&gt;commit() &amp;rarr; 트랜잭션 단위의 저장&lt;/li&gt;
&lt;li&gt;drain() &amp;rarr; 큐나 스트림 등에서 남은 것을 한 번에 소비&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 flush 의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JPA&lt;/td&gt;
&lt;td&gt;영속성 컨텍스트 &amp;rarr; DB로 변경사항 밀어넣기 (SQL만 실행, 커밋 아님)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;메모리 DB 데이터 전부 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;메모리 캐시 &amp;rarr; 디스크 반영 (fsync)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;테이블 캐시/권한 정보 리로드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파일 스트림&lt;/td&gt;
&lt;td&gt;버퍼 &amp;rarr; 디스크 강제 쓰기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✨ 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flush는 기술마다 구체적인 의미는 다르지만,&lt;br /&gt;&lt;b&gt;&quot;일시적 상태 &amp;rarr; 확정 상태로 밀어넣는 것&quot;&lt;/b&gt;, 즉 **&quot;버퍼 비우기/데이터 반영&quot;**이라는 본질은 동일합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;오호 그냥 변기물 내리기.&lt;br /&gt;라고 직관적으로 떠올리면 되겠네메모리에 머물러 있다 ~= 메서드 내부에 객체로 존재한다.&lt;br /&gt;이걸 레버 내려서,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;gt; 변기 내부의 오물 입장에서는 정화조(db)로 들어간다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;gt; 변기의 입장에서는 변기의 오물을 비운다. 가 되는거네 ?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와&amp;mdash;비유 정말 완벽합니다.&lt;br /&gt;이보다 더 직관적으로 설명하기 어려울 정도예요. 그대로 정리해볼게요:&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  flush()를 화장실로 비유하면?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  상태 대응표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개념 화장실 비유&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메모리 / 버퍼&lt;/td&gt;
&lt;td&gt;변기 내부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flush()&lt;/td&gt;
&lt;td&gt;물 내리기 (레버 내리기)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB / 디스크&lt;/td&gt;
&lt;td&gt;정화조 or 하수도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear()&lt;/td&gt;
&lt;td&gt;변기 통째로 씻어버리기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit()&lt;/td&gt;
&lt;td&gt;청소부가 와서 최종 점검하고 봉인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  의미 변환&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리에 있던 값 &amp;rarr; 아직 저장 안 됨 &amp;rarr; flush() = 디스크(DB)에 반영&lt;/li&gt;
&lt;li&gt;flush()는 &lt;b&gt;레버를 내리는 순간&lt;/b&gt; = 데이터가 흐름을 타고 정화조로 이동&lt;/li&gt;
&lt;li&gt;flush()는 실제로 &lt;b&gt;트랜잭션 커밋은 아님&lt;/b&gt; &amp;rarr; 물은 내려갔지만 &quot;완전히 닫히진 않음&quot;&lt;/li&gt;
&lt;li&gt;clear()는 변기 자체를 깨끗하게 비워서 내용물까지 사라짐&lt;/li&gt;
&lt;li&gt;commit()은 &lt;b&gt;전체 작업 완료 후 확정&lt;/b&gt;, 변기도 깨끗해졌고 물도 내려갔고 도장 찍음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이 비유의 핵심 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;flush = &quot;메모리에서 실제 대상지(DB, 디스크 등)로 밀어넣기&quot;&lt;/li&gt;
&lt;li&gt;메서드 내부 객체가 아니라면, 이미 정화조에 간 것 = &lt;b&gt;더 이상 내 로컬엔 없음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;flush() 해도 트랜잭션이 살아있으면 rollback으로 &lt;b&gt;&quot;물 다시 퍼올리기&quot;도 가능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flush() = 변기 물 내리기.&lt;br /&gt;아직 내 방(트랜잭션)은 안 닫았지만, 일단 냄새나는 건 하수구로 보낸다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>GPT Archiving</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/265</guid>
      <comments>https://99duuk.tistory.com/265#entry265comment</comments>
      <pubDate>Fri, 18 Jul 2025 13:33:38 +0900</pubDate>
    </item>
    <item>
      <title>지피티와 함께하는 통계쿼리 공부일기 2</title>
      <link>https://99duuk.tistory.com/264</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;ROLLUP&lt;/span&gt;을 이용한 부서별/직무별/성별 인원 및 급여 통계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MYSQL에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GROUP BY ... WITH ROLLUP&amp;nbsp;&lt;/b&gt;구문을 이용하여, 집계 결과로 생성되는 NULL 값들을 구분하기 위해&amp;nbsp;&lt;b&gt;GROUPING()&amp;nbsp;&lt;/b&gt;함수를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GROUPING(col) = 1&amp;nbsp;&lt;/b&gt;이면 해당 행이 ROLLUP에 의해 생성된 소계/총계 행이라는 의미다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 응용하여, NULL 대신 '전체' 등의 라벨을 표시하면 결과를 이해하기 쉬워진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ROLLUP&lt;/b&gt;은 컬럼의 순서에 따라 점진적으로 상위 수준의 총계를 계산한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; - 컬럼 리스트를 계층적으로 줄여가며 총계를 만듦&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - ex) GROUP BY A, B, C WITH ROLLUP&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A, B, C)&lt;/span&gt;: 개별 데이터.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A, B)&lt;/span&gt;: C에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A)&lt;/span&gt;: B, C에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&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;&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;rollup을 사용하면 다중 수준의 집계 결과(부분합과 총합)을 자동으로 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부서별, 직무별, 성별로 직원 수와 급여 합계를 집계하고, 각 부서별 소계(모든 직무 합계), 전체 총계를 출력해보자!&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SELECT 
    IF(GROUPING(d.dept_name)=1, '전체 부서', d.dept_name)   AS dept_name,
    IF(GROUPING(j.job_title)=1, '전체 직무', j.job_title)   AS job_title,
    IF(GROUPING(e.gender)=1,    '전체',      e.gender)      AS gender,
    COUNT(*)   AS emp_count,               -- 직원 수
    SUM(e.salary) AS total_salary          -- 급여 총합 (합계)
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
JOIN jobs j        ON e.job_id  = j.job_id
GROUP BY d.dept_name, j.job_title, e.gender WITH ROLLUP;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;2140&quot; data-origin-height=&quot;1276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yl6IF/btsOXgT1Y1v/qKqd8Td2Y8bW71dfllJY51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yl6IF/btsOXgT1Y1v/qKqd8Td2Y8bW71dfllJY51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yl6IF/btsOXgT1Y1v/qKqd8Td2Y8bW71dfllJY51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyl6IF%2FbtsOXgT1Y1v%2FqKqd8Td2Y8bW71dfllJY51%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;2140&quot; height=&quot;1276&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;1276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요로코롬 WITH ROLLUP을 사용함으로써 부서별/직무별 세부내역과 함께 부서별 합계, 전체 합계를 계층적으로 얻었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 GROUPING()을 사용해 '전체' 라벨을 붙혀 가독성을 높일 수 있긔;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 합계행을 상단에 올려버리고 싶으면&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;ORDER BY
    GROUPING(d.dept_name) DESC, d.dept_name,
    GROUPING(j.job_title) DESC, j.job_title,
    GROUPING(e.gender) DESC, e.gender;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;1982&quot; data-origin-height=&quot;1132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXH1b/btsOYGdr6cx/vzEZJk7zb2N8a1drBvHuVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXH1b/btsOYGdr6cx/vzEZJk7zb2N8a1drBvHuVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXH1b/btsOYGdr6cx/vzEZJk7zb2N8a1drBvHuVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXH1b%2FbtsOYGdr6cx%2FvzEZJk7zb2N8a1drBvHuVk%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;1982&quot; height=&quot;1132&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;1132&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;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;# if(조건, 참값, 거짓값)
# GROUPING() : 어떤 행이 실제 데이터고, 어떤 행이 집계 행인지 구분 (0: 실제 데이터, 1:NULL로 총계행)
    # 특정 컬럼이 그룹화 과정에서 집계로 인해 NULL로 표시가 되었는지 확인 (총계행은 컬럼이 NULL로 나타남)
    # rollup, cube, grouping sets 에서 사용, 집계된 행이 '실제 데이터'인지, 아니면 rollup/cube로 생성된 '집계행'인지 구분하는데 사용
    # WITH ROLLUP 또는 GROUPING SETS을 사용할 때 의미가 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;CUBE&lt;/span&gt;를 이용한 부서-직무-입사연도 조합별 통계&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&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;CUBE&lt;/b&gt;는 지정된 컬럼의 &lt;b&gt;모든&lt;/b&gt; 가능한 &lt;b&gt;조합에 대해 총계&lt;/b&gt;를 생성한다. ROLLUP은 계층적 조합만 생성하지만, CUBE는 모든 컬럼의 부분집합을 다 만들어벌임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 컬럼 리스트를 계층적으로 줄여가며 총계를 만듦&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - ex) GROUP BY CUBE(A, B, C)&amp;nbsp; (총 조합 수: 2^n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A, B, C)&lt;/span&gt;: 개별 데이터.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A, B)&lt;/span&gt;: C에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A, C)&lt;/span&gt;: B에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(B, C)&lt;/span&gt;: A에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(A)&lt;/span&gt;: B, C에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(B)&lt;/span&gt;: A, C에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(C)&lt;/span&gt;: A, B에 대한 총계.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;()&lt;/span&gt;: 전체 총계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;(2^3 = 8개 조합)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(MYSQL은 CUBE를 지원하지 않는다. 대신 GROUPING SET이나 UNION ALL로 비슷하게 구현할 수 있다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;-- CUBE(dept_name, job_title, hire_year)의 효과를 내는 유니온 쿼리

-- (dept_name, job_title, hire_year): 개별 데이터.
SELECT d.dept_name AS dept_name,
       j.job_title AS job_title,
       YEAR(e.hire_date) AS hire_year,
       COUNT(*)  AS emp_count,
       SUM(e.salary) AS total_salary
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
         JOIN jobs j        ON e.job_id  = j.job_id
GROUP BY d.dept_name, j.job_title, YEAR(e.hire_date)
UNION ALL

-- 부서-직무별 (dept_name, job_title): 연도 총계.
SELECT d.dept_name AS dept_name,
       j.job_title AS job_title,
       '전체'        AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
         JOIN jobs j        ON e.job_id  = j.job_id
GROUP BY d.dept_name, j.job_title
UNION ALL
-- 부서-연도별 (dept_name, hire_year): 직무 총계.
SELECT d.dept_name AS dept_name,
       '전체'       AS job_title,
       YEAR(e.hire_date) AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
GROUP BY d.dept_name, YEAR(e.hire_date)
UNION ALL
-- 직무-연도별 (job_title, hire_year): 부서 총계.
SELECT '전체'      AS dept_name,
       j.job_title AS job_title,
       YEAR(e.hire_date) AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN jobs j ON e.job_id = j.job_id
GROUP BY j.job_title, YEAR(e.hire_date)
UNION ALL
-- 부서별 전체 (dept_name): 직무, 연도 총계.
SELECT d.dept_name AS dept_name,
       '전체'       AS job_title,
       '전체'       AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
GROUP BY d.dept_name
UNION ALL
-- 직무별 전체 (job_title): 부서, 연도 총계.
SELECT '전체'      AS dept_name,
       j.job_title AS job_title,
       '전체'       AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN jobs j ON e.job_id = j.job_id
GROUP BY j.job_title
UNION ALL
-- 연도별 전체 (hire_year): 부서, 직무 총계.
SELECT '전체'      AS dept_name,
       '전체'      AS job_title,
       YEAR(e.hire_date) AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e
GROUP BY YEAR(e.hire_date)
UNION ALL
-- 전체 총계 (모든 부서/직무/연도)
SELECT '전체' AS dept_name, '전체' AS job_title, '전체' AS hire_year,
       COUNT(*), SUM(e.salary)
FROM employees e;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;2066&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyh1gz/btsOY6CLpkA/ltRPfjIYSa389rS50oNz30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyh1gz/btsOY6CLpkA/ltRPfjIYSa389rS50oNz30/img.png&quot; data-alt=&quot;...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyh1gz/btsOY6CLpkA/ltRPfjIYSa389rS50oNz30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdyh1gz%2FbtsOY6CLpkA%2FltRPfjIYSa389rS50oNz30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2066&quot; height=&quot;836&quot; data-origin-width=&quot;2066&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5RJLI/btsOWl2KHL0/iNwaOa0e5WEkG3NzZucluk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5RJLI/btsOWl2KHL0/iNwaOa0e5WEkG3NzZucluk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5RJLI/btsOWl2KHL0/iNwaOa0e5WEkG3NzZucluk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5RJLI%2FbtsOWl2KHL0%2FiNwaOa0e5WEkG3NzZucluk%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;2072&quot; height=&quot;1204&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;1204&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;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;GROUPING SETS&lt;/span&gt;를 활용한 다양한 조합의 소계/총계 분석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;임의의 그룹화 조합에 대한 집계&lt;/b&gt;를 수행해보게씀.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GROUPING SETS는 ROLLUP이나 CUBE와 달리,&amp;nbsp;&lt;b&gt;원하는 특정 그룹화의 조합만 선택&lt;/b&gt;하여 결과를 얻을 수 있긔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 부서별 합계와 성별 합계만 한 번에 보고 싶은 그런 상황에서 활용하믄 댐. &lt;br /&gt;(MySQL에서는 마찬가지로 GROUPING SETS를 지원하지 않으므로 UNION으로 대체하게씀 첨부터 오라클로 할 걸...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;-- GROUPING SETS ( (dept_name, gender), (dept_name), (gender), () ) 시뮬레이션
# 부서-성별별 통계
SELECT d.dept_name AS dept_name, e.gender AS gender,
       COUNT(*) AS emp_count, SUM(e.salary) AS total_salary
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
GROUP BY d.dept_name, e.gender
UNION ALL
# 부서별 전체 통계
SELECT d.dept_name AS dept_name, '전체' AS gender,
       COUNT(*), SUM(e.salary)
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
GROUP BY d.dept_name
UNION ALL
# 성별별 전체 통계
SELECT '전체' AS dept_name, e.gender AS gender,
       COUNT(*), SUM(e.salary)
FROM employees e
GROUP BY e.gender
UNION ALL
# 전체 총계
SELECT '전체' AS dept_name, '전체' AS gender,
       COUNT(*), SUM(e.salary)
FROM employees e;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;18093&quot; data-start=&quot;18032&quot;&gt;&lt;b&gt;부서별 + 성별별 세부 내역&lt;/b&gt; (첫 번째 SELECT): 각 부서의 남/여 직원 수 및 급여 합계&lt;/li&gt;
&lt;li data-end=&quot;18146&quot; data-start=&quot;18094&quot;&gt;&lt;b&gt;부서별 전체&lt;/b&gt; (두 번째 SELECT): 각 부서의 전체 직원 합계 (성별 무관)&lt;/li&gt;
&lt;li data-end=&quot;18202&quot; data-start=&quot;18147&quot;&gt;&lt;b&gt;성별별 전체&lt;/b&gt; (세 번째 SELECT): 전체 회사를 성별로 구분한 합계 (부서 무관)&lt;/li&gt;
&lt;li data-end=&quot;18240&quot; data-start=&quot;18203&quot;&gt;&lt;b&gt;전사 총계&lt;/b&gt; (네 번째 SELECT): 전체 직원 합계&lt;/li&gt;
&lt;/ul&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;1616&quot; data-origin-height=&quot;1088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTjmYX/btsOZcJEdwD/eQoqFu00Q4Q4xI2KHqMBf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTjmYX/btsOZcJEdwD/eQoqFu00Q4Q4xI2KHqMBf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTjmYX/btsOZcJEdwD/eQoqFu00Q4Q4xI2KHqMBf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTjmYX%2FbtsOZcJEdwD%2FeQoqFu00Q4Q4xI2KHqMBf0%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;1616&quot; height=&quot;1088&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;1088&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;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751265208370&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# GROUPING SETS은 사용자가 원하는 특정 그룹화 조합을 명시적으로 지정해 집계를 생성한다. 
# ROLLUP이나 CUBE와는 달리 그룹화 조합을 선택할 수 있어 유연성이 높다.
GROUP BY GROUPING SETS (
	(col1, col2, ...) -- 조합 1 
    (col1, ...) 	  -- 조합 2
    (), 			  -- 전체 총계
)

# 각 괄호는 하나의 그룹화 조합을 나타내고, 각 조합에 대해 집계함수를 적용해 결과를 생성할 수 있음&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;ROLLUP:
    계층적 총계 생성.
    예: GROUP BY A, B, C WITH ROLLUP &amp;rarr; (A, B, C), (A, B), (A), ().
    순서에 따라 제한된 조합만 생성.

CUBE:
    모든 가능한 조합 생성.
    예: CUBE(A, B, C) &amp;rarr; (A, B, C), (A, B), (A, C), (B, C), (A), (B), (C), ().
    MySQL은 직접 지원하지 않음(대신 GROUPING SETS나 UNION ALL로 구현).

GROUPING SETS:
    원하는 조합만 선택.
    예: GROUPING SETS((A, B), (A, C), ()) &amp;rarr; (A, B), (A, C), 전체 총계만 생성.
    ROLLUP이나 CUBE의 부분집합 또는 커스텀 조합 가능.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;CASE WHEN&lt;/span&gt;을 활용한 피벗(Pivot) 형태 요약표&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;영어 단어 pivot의 원뜻:&lt;br /&gt;명사: 회전축, 중심점 동사: 축을 중심으로 회전하다&lt;br /&gt;ex) 모니터암 달아서 모니터를 가로 -&amp;gt; 세로 돌리기 ~&amp;nbsp;&lt;br /&gt;&lt;br /&gt;데이터를 어떤 기준을 중심으로 회전시켜서&lt;br /&gt;행(row) &amp;rarr; 열(column) 또는 &lt;br /&gt;열 &amp;rarr; 행 &lt;br /&gt;형태로 바꾸는 것을 &quot;피벗한다(pivot)&quot;고 표현&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피벗 형태로 변환하여&amp;nbsp;&lt;b&gt;열(column) 기준의 요약표&lt;/b&gt;를 만들어보겠긔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(또) MySQL은 PIVOT 연산에 대한 전용 문법이 없으므로, CASE WHEN 과 집계함수를 조합하여 행을 열로 바꾸겠긔.&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;조건부 집계&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건에 따라 값을 집계하고, 그 결과를 별도 컬럼으로 표현하면 행 -&amp;gt; 열 변환 효과를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부서별 성별 인원 현황표를 만들어 보자!~&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT
    d.dept_name AS dept_name,
    SUM(CASE WHEN e.gender = 'M' THEN 1 ELSE 0 END) AS male_count,
    SUM(CASE WHEN e.gender = 'F' THEN 1 ELSE 0 END) AS female_count
FROM employees e
         JOIN departments d ON e.dept_id = d.dept_id
GROUP BY d.dept_name;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CASE WHEN e.gender = 'M' THEN 1 ELSE 0 END 표현식은 해당 직원이 남성일 경우 1, 아니면 0을 반환한다. SUM()을 씌우면 그 부서의 남성 직원 수 합계가 구해진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;동일한 그룹 내에서 조건별로 따로 집계한 결과를 각각 컬럼으로 표시&lt;/b&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;1272&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6TVFL/btsOXSkSQbP/JLtqPfyuLKzKA6lrSekIj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6TVFL/btsOXSkSQbP/JLtqPfyuLKzKA6lrSekIj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6TVFL/btsOXSkSQbP/JLtqPfyuLKzKA6lrSekIj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6TVFL%2FbtsOXSkSQbP%2FJLtqPfyuLKzKA6lrSekIj1%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;1272&quot; height=&quot;344&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;344&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;nbsp;&lt;/p&gt;</description>
      <category>DB</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/264</guid>
      <comments>https://99duuk.tistory.com/264#entry264comment</comments>
      <pubDate>Mon, 30 Jun 2025 17:07:06 +0900</pubDate>
    </item>
    <item>
      <title>지피티야 미안해</title>
      <link>https://99duuk.tistory.com/263</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FSTea/btsOUURHvVo/6RaXh7kg4zGtskansYU6cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FSTea/btsOUURHvVo/6RaXh7kg4zGtskansYU6cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FSTea/btsOUURHvVo/6RaXh7kg4zGtskansYU6cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFSTea%2FbtsOUURHvVo%2F6RaXh7kg4zGtskansYU6cK%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;1636&quot; height=&quot;1512&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>기타</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/263</guid>
      <comments>https://99duuk.tistory.com/263#entry263comment</comments>
      <pubDate>Sun, 29 Jun 2025 10:25:24 +0900</pubDate>
    </item>
    <item>
      <title>지피티와 함께하는 통계쿼리 공부일기</title>
      <link>https://99duuk.tistory.com/262</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;sqld 공부할땐 대충 뭐가 뭔지 기억이라도 하고 있었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기억도 안나긔&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ROLLUP, CUBE, GROUPING SETS 같은 거 지피티한테 물어봐서 대답이 돌아와도 눈에 안들어옴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 심층리서치를 사용해 차근차근 보크숩을 해보게씀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;b&gt;직원-부서-직무&lt;/b&gt;의 기본 관계와 &lt;b&gt;직원-근태 기록&amp;nbsp;&lt;/b&gt;으로 4개 테이블 잡아줬긔&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751014261419&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 기존 테이블이 있다면 삭제하고 새로 생성
DROP TABLE IF EXISTS attendance;
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS jobs;
DROP TABLE IF EXISTS departments;

-- 1) 부서 테이블
CREATE TABLE departments (
    dept_id     INT PRIMARY KEY AUTO_INCREMENT,
    dept_name   VARCHAR(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 2) 직무 테이블
CREATE TABLE jobs (
    job_id      INT PRIMARY KEY AUTO_INCREMENT,
    job_title   VARCHAR(50) NOT NULL,
    min_salary  INT NOT NULL,
    max_salary  INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 3) 직원 테이블 (부서, 직무 FK 참조)
CREATE TABLE employees (
    employee_id INT PRIMARY KEY AUTO_INCREMENT,
    first_name  VARCHAR(50),
    last_name   VARCHAR(50),
    gender      CHAR(1),
    hire_date   DATE,
    dept_id     INT,
    job_id      INT,
    salary      INT,
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id),
    FOREIGN KEY (job_id)  REFERENCES jobs(job_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 4) 근태 테이블 (직원 FK 참조)
CREATE TABLE attendance (
    att_id      INT PRIMARY KEY AUTO_INCREMENT,
    employee_id INT,
    att_date    DATE,
    status      CHAR(1),
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1751014276373&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 부서 데이터 삽입 (예: 5개 부서)
INSERT INTO departments(dept_id, dept_name) VALUES
(1, 'HR'), (2, 'IT'), (3, 'Sales'), (4, 'Finance'), (5, 'Marketing');

-- 직무 데이터 삽입 (예: 5개 직무 및 급여 범위)
INSERT INTO jobs(job_id, job_title, min_salary, max_salary) VALUES
(1, 'Manager',     70000, 120000),
(2, 'Engineer',    60000, 100000),
(3, 'Analyst',     50000,  90000),
(4, 'Clerk',       30000,  50000),
(5, 'Salesperson', 40000,  80000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 데이터를 넣어줬긔&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 데이터는 지피티가 그냥 줄까? 하는거&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로시저로 달라고 했긔&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;프로시저 gen_sample_data의 로직:&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;4314&quot; data-end=&quot;5037&quot;&gt;
&lt;li data-start=&quot;4314&quot; data-end=&quot;4353&quot;&gt;&lt;i&gt;입력 파라미터로 생성할 직원 수를 받습니다 (p_count).&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4354&quot; data-end=&quot;4753&quot;&gt;&lt;i&gt;1부터 p_count까지 루프를 돌면서 각 회차마다 새로운 직원 레코드를 생성합니다.&lt;/i&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;4408&quot; data-end=&quot;4753&quot;&gt;
&lt;li data-start=&quot;4408&quot; data-end=&quot;4449&quot;&gt;&lt;i&gt;무작위 부서(dept_id)와 직무(job_id)를 선택합니다.&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4452&quot; data-end=&quot;4498&quot;&gt;&lt;i&gt;직무에 대응하는 급여 범위(min~max) 내에서 랜덤 급여 금액을 책정합니다.&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4501&quot; data-end=&quot;4533&quot;&gt;&lt;i&gt;성별은 50% 확률로 'M' 또는 'F'로 지정합니다.&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4536&quot; data-end=&quot;4594&quot;&gt;&lt;i&gt;입사일(hire_date)은 2015-01-01부터 오늘 날짜 사이에서 무작위 날짜를 생성합니다.&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4597&quot; data-end=&quot;4753&quot;&gt;&lt;i&gt;직원의 이름은 예시용으로 몇 가지 남녀 이름 목록에서 무작위로 선택합니다 (남성 이름: John, Mike, Robert / 여성 이름: Jane, Emily, Anna 등). 성(last_name)은 Kim, Lee, Park, Choi, Smith 등에서 임의 선택합니다.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start=&quot;4754&quot; data-end=&quot;4819&quot;&gt;&lt;i&gt;직원 레코드를 INSERT한 후 LAST_INSERT_ID()를 이용해 방금 생성된 직원의 ID를 가져옵니다.&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4820&quot; data-end=&quot;4988&quot;&gt;&lt;i&gt;이어서 해당 직원의 근태 기록을 5~15건 사이의 랜덤 개수로 생성합니다.&lt;/i&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;4866&quot; data-end=&quot;4988&quot;&gt;
&lt;li data-start=&quot;4866&quot; data-end=&quot;4936&quot;&gt;&lt;i&gt;각 근태 레코드에 대해, 직원의 입사일 이후 오늘 사이의 임의 날짜를 선택합니다. (※ 입사일 이전 날짜는 생성하지 않음)&lt;/i&gt;&lt;/li&gt;
&lt;li data-start=&quot;4939&quot; data-end=&quot;4988&quot;&gt;&lt;i&gt;근태 상태는 80% 확률로 'P'(출석), 20% 확률로 'A'(결석)로 지정합니다.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start=&quot;4989&quot; data-end=&quot;5037&quot;&gt;&lt;i&gt;루프를 완료하면 지정한 수의 직원과 다수의 근태 레코드가 데이터베이스에 삽입됩니다.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;5039&quot; data-end=&quot;5135&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;아래는 해당 프로시저 생성 및 실행 스크립트입니다. 프로시저를 생성한 후, 예시로 100명의 직원을 생성하도록 호출합니다 (원하는 데이터 양에 따라 파라미터를 조정 가능):&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;5039&quot; data-end=&quot;5135&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;5039&quot; data-end=&quot;5135&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751014438542&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELIMITER $$
CREATE PROCEDURE gen_sample_data(IN p_count INT)   #프로시저 선언부, p_count : 몇 명의 직원을 생성할지 받는 파라미터, $: 구분자로 내부의 ;를 무시하고 문장 끝이 END $ 라는 것을 알려줌
BEGIN
    DECLARE i INT DEFAULT 1;        # 직원 생성 루프용 변수
    DECLARE dept_count INT;         # 전체 부서 수 저장 변수
    DECLARE job_count INT;          # 전체 직무 수 저장 변수
    DECLARE rand_dept INT;          # 무직위로 선택할 부서 ID
    DECLARE rand_job INT;           # 무직위로 선택할 직무 ID
    DECLARE min_sal INT;            # 직무의 최소 급여
    DECLARE max_sal INT;            # 직무의 최대 급여
    DECLARE rand_gender CHAR(1);    # 성별 (M/F)
    DECLARE fn VARCHAR(50);         # 이름
    DECLARE ln VARCHAR(50);         # 성
    DECLARE hire_dt DATE;           # 입사일
    DECLARE empId INT;              # 생성된 직원의 ID
    DECLARE att_count INT;          # 생성할 근태 데이터 수
    DECLARE days_range INT;         # 입사일~오늘까지 일수 차이
    DECLARE days_offset INT;        # 랜덤 근태 날짜를 위한 오프셋
    DECLARE att_dt DATE;            # 생성할 근태 날짜
    DECLARE att_status CHAR(1);     # 출결 상태 (P/A)

    # 총 부서 수와 직무 수 조회
    SELECT COUNT(*) INTO dept_count FROM departments;
    SELECT COUNT(*) INTO job_count FROM jobs;

    WHILE i &amp;lt;= p_count DO   # 직무 수만큼 반복
        # 무작위 부서 및 직무 선택
        SET rand_dept = FLOOR(1 + RAND() * dept_count);
        SET rand_job = FLOOR(1 + RAND() * job_count);

        -- 선택된 직무의 급여 범위 조회
        SELECT min_salary, max_salary INTO min_sal, max_sal
        FROM jobs WHERE job_id = rand_job;

        # 성별 무작위 선택
        IF RAND() &amp;lt; 0.5 THEN
            SET rand_gender = 'M';
        ELSE
            SET rand_gender = 'F';
        END IF;

        # 성별에 따라 이름 무작위 선택
        IF rand_gender = 'M' THEN
            CASE MOD(i, 3)
                WHEN 0 THEN SET fn = 'John';
                WHEN 1 THEN SET fn = 'Mike';
                ELSE SET fn = 'Robert';
            END CASE;
        ELSE
            CASE MOD(i, 3)
                WHEN 0 THEN SET fn = 'Jane';
                WHEN 1 THEN SET fn = 'Emily';
                ELSE SET fn = 'Anna';
            END CASE;
        END IF;

        # 성 무작위 선택
        CASE MOD(i, 5)
            WHEN 0 THEN SET ln = 'Kim';
            WHEN 1 THEN SET ln = 'LEE';
            WHEN 2 THEN SET ln = 'Park';
            WHEN 3 THEN SET ln = 'Choi';
            WHEN 4 THEN SET ln = 'Smith';
        END CASE;

        # 입사일: 2015년 1월 1일부터 오늘까지 무작위
        SET hire_dt = DATE_ADD('2015-01-01', INTERVAL FLOOR(RAND() * 4018) DAY);

        # 직원 데이터 삽입
        INSERT INTO employees(dept_id, job_id, first_name, last_name, gender, hire_date, salary)
            VALUES (rand_dept, rand_job, fn, ln, rand_gender, hire_dt, FLOOR(min_sal + RAND() * (max_sal - min_sal + 1)));

        # 방금 삽입한 직원 ID 저장
        SET empId = LAST_INSERT_ID();

        # 5~15개 사이의 무작위 근태 데이터 개수 설정
        SET att_count = FLOOR(RAND() * 11) + 5;

        # 입사일 기준 오늘까지 일수 계산
        SET days_range = DATEDIFF(CURDATE(), hire_dt);
        IF days_range &amp;lt; 0 THEN
            SET days_range = 0;
        END IF;

        WHILE att_count &amp;gt; 0 DO  # 근태 데이터 수만큼 반복
            IF days_range &amp;gt; 0 THEN
                SET days_offset = FLOOR(RAND() * (days_range + 1));
            ELSE
                SET days_offset = 0;
            END IF;
            SET att_dt = DATE_ADD(hire_dt, INTERVAL days_offset DAY);    # 근태 일자 생성

            # 출결 상태 무작위 (80% 출석, 20% 결석)
            IF RAND() &amp;lt; 0.2 THEN
                SET att_status = 'A';
            ELSE
                SET att_status = 'P';
            END IF;

            # 근태 데이터 삽입
            INSERT INTO attendance(employee_id, att_date, status) VALUES (empId, att_dt, att_status);

            # 근태 카운트 감소
            SET att_count = att_count - 1;
        END WHILE;

        # 다음 직원
        SET i = i + 1;
        END WHILE;
    END $$
    DELIMITER ;



    # 직원 100명 생성
    CALL gen_sample_data(100);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짜자잔~ (짜잔형 아님)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;TIP: 대량의 데이터를 생성할 때 &lt;br /&gt;RAND() 함수를 사용하면 편리합니다. 위에서는 부서/직무 선택, 성별 결정, 날짜 오프셋 결정 등에 &lt;br /&gt;RAND()를 활용했습니다. MySQL 8.0 기준으로 이러한 프로시저와 함수 사용이 가능하며, 데이터 양을 조절하여 다양한 시나리오를 실습할 수 있습니다 datacamp.com.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 준비했긔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 본격저긍로 통계쿼리를 연습해보겠긔&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다음 포스팅에서 건강하게 만납시다~&amp;nbsp;&lt;/p&gt;</description>
      <category>DB</category>
      <author>99duuk</author>
      <guid isPermaLink="true">https://99duuk.tistory.com/262</guid>
      <comments>https://99duuk.tistory.com/262#entry262comment</comments>
      <pubDate>Fri, 27 Jun 2025 17:55:40 +0900</pubDate>
    </item>
  </channel>
</rss>