<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>삼시세끼코딩</title>
    <link>https://threemealsofcoding.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 23:28:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코딩금융치료</managingEditor>
    <item>
      <title>랜덤포레스트 쉽게 이해하기: 여러 나무가 함께 내리는 똑똑한 결정</title>
      <link>https://threemealsofcoding.tistory.com/59</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (1).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cS6cLB/dJMcaakM0DQ/MlT78X6X439e5Qi7rKvJ4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cS6cLB/dJMcaakM0DQ/MlT78X6X439e5Qi7rKvJ4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cS6cLB/dJMcaakM0DQ/MlT78X6X439e5Qi7rKvJ4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcS6cLB%2FdJMcaakM0DQ%2FMlT78X6X439e5Qi7rKvJ4K%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;1672&quot; height=&quot;941&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (1).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;893&quot; data-start=&quot;837&quot; data-ke-size=&quot;size16&quot;&gt;머신러닝을 공부하다 보면 &lt;b&gt;랜덤포레스트(Random Forest)&lt;/b&gt;라는 말을 자주 듣게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;895&quot; data-ke-size=&quot;size16&quot;&gt;이름만 보면 조금 어렵게 느껴집니다.&lt;/p&gt;
&lt;p data-end=&quot;966&quot; data-start=&quot;917&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;랜덤은 무작위라는 뜻이고, 포레스트는 숲이라는 뜻인데, 머신러닝에서 숲이 왜 나올까?&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;987&quot; data-start=&quot;968&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 이렇게 느낄 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1018&quot; data-start=&quot;989&quot; data-ke-size=&quot;size16&quot;&gt;하지만 랜덤포레스트의 핵심 개념은 생각보다 쉽습니다.&lt;/p&gt;
&lt;p data-end=&quot;1096&quot; data-start=&quot;1020&quot; data-ke-size=&quot;size16&quot;&gt;간단히 말하면 랜덤포레스트는 &lt;b&gt;여러 개의 결정트리가 함께 의견을 내고, 그 의견을 모아서 최종 결정을 내리는 머신러닝 방법&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;1124&quot; data-start=&quot;1098&quot; data-ke-size=&quot;size16&quot;&gt;초등학생도 이해할 수 있게 비유하면 이렇습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1182&quot; data-start=&quot;1126&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1182&quot; data-start=&quot;1128&quot; data-ke-size=&quot;size16&quot;&gt;한 명의 친구에게만 물어보는 것이 아니라, 여러 친구에게 물어본 뒤 다수결로 결정하는 방법입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;1255&quot; data-start=&quot;1184&quot; data-ke-size=&quot;size16&quot;&gt;한 명이 판단하면 틀릴 수 있습니다.&lt;br /&gt;하지만 여러 명이 각자 생각하고, 그 결과를 모으면 더 안정적인 답을 낼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1271&quot; data-start=&quot;1257&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트도 똑같습니다.&lt;/p&gt;
&lt;p data-end=&quot;1341&quot; data-start=&quot;1273&quot; data-ke-size=&quot;size16&quot;&gt;하나의 나무가 아니라, 여러 나무가 함께 판단합니다.&lt;br /&gt;그래서 이름도 &lt;b&gt;랜덤포레스트&lt;/b&gt;, 즉 &lt;b&gt;무작위 숲&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-end=&quot;1346&quot; data-start=&quot;1343&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1369&quot; data-start=&quot;1348&quot; data-section-id=&quot;j61pg7&quot; data-ke-size=&quot;size26&quot;&gt;1. 먼저 결정트리부터 이해해보자&lt;/h2&gt;
&lt;p data-end=&quot;1422&quot; data-start=&quot;1371&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트를 이해하려면 먼저 &lt;b&gt;결정트리(Decision Tree)&lt;/b&gt;를 알아야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;1454&quot; data-start=&quot;1424&quot; data-ke-size=&quot;size16&quot;&gt;결정트리는 말 그대로 &lt;b&gt;결정을 내리는 나무&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;1490&quot; data-start=&quot;1456&quot; data-ke-size=&quot;size16&quot;&gt;질문을 하나씩 따라가다 보면 마지막에 답이 나오는 구조입니다.&lt;/p&gt;
&lt;p data-end=&quot;1519&quot; data-start=&quot;1492&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1519&quot; data-start=&quot;1492&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 동물을 맞히는 문제를 생각해보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;1558&quot; data-start=&quot;1521&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;털이 있나요?&amp;rdquo;&lt;br /&gt;&amp;ldquo;다리가 4개인가요?&amp;rdquo;&lt;br /&gt;&amp;ldquo;날개가 있나요?&amp;rdquo;&lt;/p&gt;
&lt;p data-end=&quot;1610&quot; data-start=&quot;1560&quot; data-ke-size=&quot;size16&quot;&gt;이런 질문을 차례대로 하다 보면 강아지, 토끼, 새, 물고기 같은 답을 찾을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1677&quot; data-start=&quot;1612&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1705&quot; data-start=&quot;1679&quot; data-ke-size=&quot;size16&quot;&gt;위 그림처럼 결정트리는 질문을 따라 내려갑니다.&lt;/p&gt;
&lt;p data-end=&quot;1721&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_55.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oL58F/dJMcafl7dSY/V8amrf7c07NTye2xmtlLs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oL58F/dJMcafl7dSY/V8amrf7c07NTye2xmtlLs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oL58F/dJMcafl7dSY/V8amrf7c07NTye2xmtlLs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoL58F%2FdJMcafl7dSY%2FV8amrf7c07NTye2xmtlLs0%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;1672&quot; height=&quot;941&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_55.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;털이 있나요?&lt;/span&gt;&lt;br /&gt;&lt;span&gt; ├─ 예 &amp;rarr; 다리가 4개인가요?&lt;/span&gt;&lt;br /&gt;&lt;span&gt; │ ├─ 예 &amp;rarr; 강아지&lt;/span&gt;&lt;br /&gt;&lt;span&gt; │ └─ 아니오 &amp;rarr; 토끼&lt;/span&gt;&lt;br /&gt;&lt;span&gt; └─ 아니오 &amp;rarr; 날개가 있나요?&lt;/span&gt;&lt;br /&gt;&lt;span&gt; ├─ 예 &amp;rarr; 새&lt;/span&gt;&lt;br /&gt;&lt;span&gt; └─ 아니오 &amp;rarr; 물고기&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;1896&quot; data-start=&quot;1855&quot; data-ke-size=&quot;size16&quot;&gt;질문이 나뭇가지처럼 갈라지기 때문에 &lt;b&gt;트리(Tree)&lt;/b&gt;라고 부릅니다.&lt;/p&gt;
&lt;p data-end=&quot;1966&quot; data-start=&quot;1898&quot; data-ke-size=&quot;size16&quot;&gt;결정트리는 이해하기 쉽다는 장점이 있습니다.&lt;br /&gt;질문을 따라가면 왜 그런 답이 나왔는지 어느 정도 알 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-end=&quot;2000&quot; data-start=&quot;1968&quot; data-ke-size=&quot;size16&quot;&gt;하지만 결정트리 하나만 사용하면 문제가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;2079&quot; data-start=&quot;2002&quot; data-ke-size=&quot;size16&quot;&gt;하나의 나무가 너무 자기 생각만 강하게 가질 수 있습니다.&lt;br /&gt;또는 학습한 데이터에만 너무 잘 맞고, 새로운 데이터에는 약할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;2126&quot; data-start=&quot;2081&quot; data-ke-size=&quot;size16&quot;&gt;그래서 여러 결정트리를 모아서 더 안정적인 결과를 만들자는 생각이 나오게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;2145&quot; data-start=&quot;2128&quot; data-ke-size=&quot;size16&quot;&gt;그것이 바로 랜덤포레스트입니다.&lt;/p&gt;
&lt;hr data-end=&quot;2150&quot; data-start=&quot;2147&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2183&quot; data-start=&quot;2152&quot; data-section-id=&quot;ht18eq&quot; data-ke-size=&quot;size26&quot;&gt;2. 랜덤포레스트는 결정트리 여러 개가 모인 숲이다&lt;/h2&gt;
&lt;p data-end=&quot;2208&quot; data-start=&quot;2185&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 하나의 결정트리가 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;2227&quot; data-start=&quot;2210&quot; data-ke-size=&quot;size16&quot;&gt;여러 개의 결정트리를 만듭니다.&lt;/p&gt;
&lt;p data-end=&quot;2252&quot; data-start=&quot;2229&quot; data-ke-size=&quot;size16&quot;&gt;그리고 각각의 결정트리가 따로 판단합니다.&lt;/p&gt;
&lt;p data-end=&quot;2252&quot; data-start=&quot;2229&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2298&quot; data-start=&quot;2254&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 어떤 사진이 고양이인지 강아지인지 맞히는 문제가 있다고 해보겠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1번 나무: 고양이&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2번 나무: 고양이&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3번 나무: 강아지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4번 나무: 고양이&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5번 나무: 고양이&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;2393&quot; data-start=&quot;2368&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 고양이라고 말한 나무가 더 많습니다.&lt;/p&gt;
&lt;p data-end=&quot;2427&quot; data-start=&quot;2395&quot; data-ke-size=&quot;size16&quot;&gt;그래서 랜덤포레스트는 최종 답을 &lt;b&gt;고양이&lt;/b&gt;로 정합니다.&lt;/p&gt;
&lt;p data-end=&quot;2479&quot; data-start=&quot;2429&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 여러 나무의 의견을 모아서 최종 결정을 내리는 것이 랜덤포레스트의 기본 원리입니다.&lt;/p&gt;
&lt;p data-end=&quot;2509&quot; data-start=&quot;2481&quot; data-ke-size=&quot;size16&quot;&gt;분류 문제에서는 보통 &lt;b&gt;다수결 투표&lt;/b&gt;를 합니다.&lt;/p&gt;
&lt;p data-end=&quot;2561&quot; data-start=&quot;2511&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 고양이냐 강아지냐, 합격이냐 불합격이냐, 스팸 메일이냐 아니냐 같은 문제입니다.&lt;/p&gt;
&lt;p data-end=&quot;2600&quot; data-start=&quot;2563&quot; data-ke-size=&quot;size16&quot;&gt;반대로 숫자를 예측하는 문제에서는 보통 &lt;b&gt;평균값&lt;/b&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-end=&quot;2600&quot; data-start=&quot;2563&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2625&quot; data-start=&quot;2602&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 집값을 예측한다고 해보겠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;1번 나무 예측: 3억 원&lt;/span&gt;&lt;br /&gt;&lt;span&gt;2번 나무 예측: 3억 2천만 원&lt;/span&gt;&lt;br /&gt;&lt;span&gt;3번 나무 예측: 2억 8천만 원&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;2733&quot; data-start=&quot;2693&quot; data-ke-size=&quot;size16&quot;&gt;이런 식으로 여러 나무가 예측한 값을 평균 내서 최종 예측값을 정합니다.&lt;/p&gt;
&lt;hr data-end=&quot;2738&quot; data-start=&quot;2735&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2752&quot; data-start=&quot;2740&quot; data-section-id=&quot;1o6ez6n&quot; data-ke-size=&quot;size26&quot;&gt;3. 앙상블이란?&lt;/h2&gt;
&lt;p data-end=&quot;2786&quot; data-start=&quot;2754&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트를 이해할 때 꼭 알아야 하는 용어가 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;2813&quot; data-start=&quot;2788&quot; data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;앙상블(Ensemble)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;2839&quot; data-start=&quot;2815&quot; data-ke-size=&quot;size16&quot;&gt;앙상블은 원래 음악에서 많이 쓰는 말입니다.&lt;/p&gt;
&lt;p data-end=&quot;2903&quot; data-start=&quot;2841&quot; data-ke-size=&quot;size16&quot;&gt;피아노 혼자 연주하는 것도 좋지만, 피아노, 바이올린, 첼로, 플루트가 함께 연주하면 더 풍성한 음악이 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;2919&quot; data-start=&quot;2905&quot; data-ke-size=&quot;size16&quot;&gt;머신러닝에서도 비슷합니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2969&quot; data-start=&quot;2921&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2969&quot; data-start=&quot;2923&quot; data-ke-size=&quot;size16&quot;&gt;여러 개의 모델을 함께 사용해서 더 좋은 결과를 만드는 방법을 앙상블이라고 합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;3000&quot; data-start=&quot;2971&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 여러 개의 결정트리를 함께 사용합니다.&lt;/p&gt;
&lt;p data-end=&quot;3033&quot; data-start=&quot;3002&quot; data-ke-size=&quot;size16&quot;&gt;그래서 랜덤포레스트는 대표적인 &lt;b&gt;앙상블 모델&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;3128&quot; data-start=&quot;3035&quot; data-ke-size=&quot;size16&quot;&gt;한 명의 친구보다 여러 친구의 의견을 듣는 것이 더 안정적일 때가 있습니다.&lt;br /&gt;한 명의 전문가보다 여러 전문가의 의견을 모으면 더 균형 잡힌 판단을 할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3146&quot; data-start=&quot;3130&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트도 마찬가지입니다.&lt;/p&gt;
&lt;p data-end=&quot;3192&quot; data-start=&quot;3148&quot; data-ke-size=&quot;size16&quot;&gt;하나의 결정트리가 실수하더라도 다른 결정트리들이 그 실수를 보완할 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;3197&quot; data-start=&quot;3194&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3210&quot; data-start=&quot;3199&quot; data-section-id=&quot;w948u9&quot; data-ke-size=&quot;size26&quot;&gt;4. 배깅이란?&lt;/h2&gt;
&lt;p data-end=&quot;3251&quot; data-start=&quot;3212&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트에서 또 중요한 용어가 &lt;b&gt;배깅(Bagging)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;3273&quot; data-start=&quot;3253&quot; data-ke-size=&quot;size16&quot;&gt;배깅은 쉽게 말하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;3319&quot; data-start=&quot;3275&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;3319&quot; data-start=&quot;3277&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 여러 번 랜덤하게 뽑아서 여러 모델에게 따로 공부시키는 방법입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;3338&quot; data-start=&quot;3321&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 쉽게 설명해보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;3361&quot; data-start=&quot;3340&quot; data-ke-size=&quot;size16&quot;&gt;반에 학생이 5명 있다고 해보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;3416&quot; data-start=&quot;3363&quot; data-ke-size=&quot;size16&quot;&gt;선생님이 모든 학생에게 완전히 똑같은 문제지를 나눠주면, 학생들의 생각이 비슷해질 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3447&quot; data-start=&quot;3418&quot; data-ke-size=&quot;size16&quot;&gt;하지만 조금씩 다른 문제지를 나눠주면 어떻게 될까요?&lt;/p&gt;
&lt;p data-end=&quot;3509&quot; data-start=&quot;3449&quot; data-ke-size=&quot;size16&quot;&gt;학생마다 보는 문제가 조금씩 다릅니다.&lt;br /&gt;그래서 각자 다른 방식으로 공부하고, 다른 답을 낼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3540&quot; data-start=&quot;3511&quot; data-ke-size=&quot;size16&quot;&gt;마지막에는 학생들의 답을 모아서 최종 답을 정합니다.&lt;/p&gt;
&lt;p data-end=&quot;3556&quot; data-start=&quot;3542&quot; data-ke-size=&quot;size16&quot;&gt;이것이 배깅의 느낌입니다.&lt;/p&gt;
&lt;p data-end=&quot;3633&quot; data-start=&quot;3558&quot; 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-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (3).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/peqSF/dJMcaf0Gc2q/QKYPsNMWgRxE2oty90eW31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/peqSF/dJMcaf0Gc2q/QKYPsNMWgRxE2oty90eW31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/peqSF/dJMcaf0Gc2q/QKYPsNMWgRxE2oty90eW31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpeqSF%2FdJMcaf0Gc2q%2FQKYPsNMWgRxE2oty90eW31%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;1672&quot; height=&quot;941&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (3).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3633&quot; data-start=&quot;3558&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3633&quot; data-start=&quot;3558&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3684&quot; data-start=&quot;3635&quot; data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 전체 데이터가 있고, 각 나무가 조금씩 다른 데이터를 받아서 공부합니다.&lt;/p&gt;
&lt;p data-end=&quot;3703&quot; data-start=&quot;3686&quot; data-ke-size=&quot;size16&quot;&gt;그리고 각 나무가 답을 냅니다.&lt;/p&gt;
&lt;p data-end=&quot;3727&quot; data-start=&quot;3705&quot; data-ke-size=&quot;size16&quot;&gt;마지막에는 모든 답을 모아서 투표합니다.&lt;/p&gt;
&lt;p data-end=&quot;3775&quot; data-start=&quot;3729&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 하나의 나무만 사용할 때보다 더 안정적인 결과를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3788&quot; data-start=&quot;3777&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 이렇습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;앙상블: 여러 모델의 힘을 합치는 방법&lt;/span&gt;&lt;br /&gt;&lt;span&gt;배깅: 데이터를 랜덤하게 여러 번 뽑아 여러 모델을 학습시키는 방법&lt;/span&gt;&lt;br /&gt;&lt;span&gt;랜덤포레스트: 배깅을 이용해 여러 결정트리를 만든 앙상블 모델&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;hr data-end=&quot;3901&quot; data-start=&quot;3898&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3933&quot; data-start=&quot;3903&quot; data-section-id=&quot;1ypdp7&quot; data-ke-size=&quot;size26&quot;&gt;5. 랜덤포레스트에서 &amp;ldquo;랜덤&amp;rdquo;은 무엇이 랜덤일까?&lt;/h2&gt;
&lt;p data-end=&quot;3975&quot; data-start=&quot;3935&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트에는 이름 그대로 &lt;b&gt;랜덤&lt;/b&gt;, 즉 무작위 요소가 들어갑니다.&lt;/p&gt;
&lt;p data-end=&quot;3992&quot; data-start=&quot;3977&quot; data-ke-size=&quot;size16&quot;&gt;크게 두 가지가 랜덤입니다.&lt;/p&gt;
&lt;p data-end=&quot;4022&quot; data-start=&quot;3994&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째는 &lt;b&gt;데이터를 랜덤으로 뽑는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;4090&quot; data-start=&quot;4024&quot; data-ke-size=&quot;size16&quot;&gt;모든 결정트리가 완전히 같은 데이터를 보고 공부하지 않습니다.&lt;br /&gt;각각의 나무가 조금씩 다른 데이터를 보고 공부합니다.&lt;/p&gt;
&lt;p data-end=&quot;4128&quot; data-start=&quot;4092&quot; data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;질문에 사용할 특징을 랜덤으로 고르는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;4153&quot; data-start=&quot;4130&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 과일을 구분한다고 해보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;4177&quot; data-start=&quot;4155&quot; data-ke-size=&quot;size16&quot;&gt;사용할 수 있는 특징은 여러 가지입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;색깔&lt;/span&gt;&lt;br /&gt;&lt;span&gt;크기&lt;/span&gt;&lt;br /&gt;&lt;span&gt;무게&lt;/span&gt;&lt;br /&gt;&lt;span&gt;향기&lt;/span&gt;&lt;br /&gt;&lt;span&gt;껍질 모양&lt;/span&gt;&lt;br /&gt;&lt;span&gt;당도&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;4238&quot; data-start=&quot;4213&quot; data-ke-size=&quot;size16&quot;&gt;결정트리는 이 특징들을 보고 질문을 만듭니다.&lt;/p&gt;
&lt;p data-end=&quot;4309&quot; data-start=&quot;4240&quot; data-ke-size=&quot;size16&quot;&gt;하지만 랜덤포레스트에서는 모든 나무가 매번 똑같은 특징만 보지 않습니다.&lt;br /&gt;나무마다 일부 특징을 랜덤하게 보고 판단합니다.&lt;/p&gt;
&lt;p data-end=&quot;4347&quot; data-start=&quot;4311&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 모든 나무가 너무 비슷해지는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4396&quot; data-start=&quot;4349&quot; data-ke-size=&quot;size16&quot;&gt;즉, 랜덤포레스트는 여러 나무가 서로 조금씩 다른 관점으로 문제를 바라보게 만듭니다.&lt;/p&gt;
&lt;p data-end=&quot;4422&quot; data-start=&quot;4398&quot; data-ke-size=&quot;size16&quot;&gt;그래서 더 다양한 의견을 모을 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;4427&quot; data-start=&quot;4424&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4449&quot; data-start=&quot;4429&quot; data-section-id=&quot;1b2238u&quot; data-ke-size=&quot;size26&quot;&gt;6. n_estimators란?&lt;/h2&gt;
&lt;p data-end=&quot;4505&quot; data-start=&quot;4451&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트를 실제 코드로 사용하다 보면 n_estimators라는 설정을 자주 보게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;4522&quot; data-start=&quot;4507&quot; data-ke-size=&quot;size16&quot;&gt;처음 보면 낯선 단어입니다.&lt;/p&gt;
&lt;p data-end=&quot;4537&quot; data-start=&quot;4524&quot; data-ke-size=&quot;size16&quot;&gt;하지만 뜻은 간단합니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;4584&quot; data-start=&quot;4539&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;4584&quot; data-start=&quot;4541&quot; data-ke-size=&quot;size16&quot;&gt;n_estimators는 랜덤포레스트 안에 들어가는 결정트리의 개수입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;4639&quot; data-start=&quot;4586&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 n_estimators=100이라고 하면 결정트리 100개를 만들겠다는 뜻입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&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 id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;RandomForestClassifier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n_estimators&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;4716&quot; data-start=&quot;4697&quot; data-ke-size=&quot;size16&quot;&gt;이 코드는 이렇게 이해하면 됩니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;4749&quot; data-start=&quot;4718&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;4749&quot; data-start=&quot;4720&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;나무 100개로 이루어진 랜덤포레스트를 만들겠다.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;4841&quot; data-start=&quot;4751&quot; 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-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (4).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDBZjW/dJMcadaGDCy/8KLwBXC1zITQkV4OhfWIH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDBZjW/dJMcadaGDCy/8KLwBXC1zITQkV4OhfWIH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDBZjW/dJMcadaGDCy/8KLwBXC1zITQkV4OhfWIH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDBZjW%2FdJMcadaGDCy%2F8KLwBXC1zITQkV4OhfWIH0%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;1672&quot; height=&quot;941&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (4).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;4841&quot; data-start=&quot;4751&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4876&quot; data-start=&quot;4843&quot; data-ke-size=&quot;size16&quot;&gt;나무가 1개만 있으면 한 명의 의견만 듣는 것과 비슷합니다.&lt;/p&gt;
&lt;p data-end=&quot;4913&quot; data-start=&quot;4878&quot; data-ke-size=&quot;size16&quot;&gt;나무가 10개 있으면 조금 더 다양한 의견을 들을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4947&quot; data-start=&quot;4915&quot; data-ke-size=&quot;size16&quot;&gt;나무가 100개 있으면 더 많은 의견을 모을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4972&quot; data-start=&quot;4949&quot; data-ke-size=&quot;size16&quot;&gt;하지만 무조건 많다고 좋은 것은 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;5044&quot; data-start=&quot;4974&quot; data-ke-size=&quot;size16&quot;&gt;나무가 많아질수록 계산 시간이 오래 걸립니다.&lt;br /&gt;컴퓨터가 더 많은 나무를 만들고, 더 많은 예측을 계산해야 하기 때문입니다.&lt;/p&gt;
&lt;p data-end=&quot;5074&quot; data-start=&quot;5046&quot; data-ke-size=&quot;size16&quot;&gt;그래서 보통 처음에는 이런 값들을 많이 사용합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;n_estimators=100&lt;/span&gt;&lt;br /&gt;&lt;span&gt;n_estimators=200&lt;/span&gt;&lt;br /&gt;&lt;span&gt;n_estimators=500&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;5188&quot; data-start=&quot;5140&quot; data-ke-size=&quot;size16&quot;&gt;처음 공부할 때는 n_estimators=100 정도로 시작하면 이해하기 쉽습니다.&lt;/p&gt;
&lt;hr data-end=&quot;5193&quot; data-start=&quot;5190&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5207&quot; data-start=&quot;5195&quot; data-section-id=&quot;12osp7v&quot; data-ke-size=&quot;size26&quot;&gt;7. 가지치기란?&lt;/h2&gt;
&lt;p data-end=&quot;5242&quot; data-start=&quot;5209&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;b&gt;가지치기(Pruning)&lt;/b&gt;를 알아보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;5269&quot; data-start=&quot;5244&quot; data-ke-size=&quot;size16&quot;&gt;가지치기는 실제 나무에서도 사용하는 말입니다.&lt;/p&gt;
&lt;p data-end=&quot;5312&quot; data-start=&quot;5271&quot; data-ke-size=&quot;size16&quot;&gt;나무가 너무 복잡하게 자라면 불필요한 가지를 잘라내서 깔끔하게 정리합니다.&lt;/p&gt;
&lt;p data-end=&quot;5328&quot; data-start=&quot;5314&quot; data-ke-size=&quot;size16&quot;&gt;결정트리에서도 비슷합니다.&lt;/p&gt;
&lt;p data-end=&quot;5371&quot; data-start=&quot;5330&quot; data-ke-size=&quot;size16&quot;&gt;결정트리가 너무 깊고 복잡해지면 훈련 데이터는 아주 잘 맞힐 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5409&quot; data-start=&quot;5373&quot; data-ke-size=&quot;size16&quot;&gt;하지만 새로운 데이터가 들어오면 오히려 잘 못 맞힐 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5444&quot; data-start=&quot;5411&quot; data-ke-size=&quot;size16&quot;&gt;이것을 &lt;b&gt;과적합(Overfitting)&lt;/b&gt;이라고 합니다.&lt;/p&gt;
&lt;p data-end=&quot;5489&quot; data-start=&quot;5446&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면, 문제의 원리를 이해한 것이 아니라 답만 외운 상태와 비슷합니다.&lt;/p&gt;
&lt;p data-end=&quot;5535&quot; data-start=&quot;5491&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생이 수학 개념을 이해하지 않고 문제집 답만 외웠다고 해보겠습니다.&lt;/p&gt;
&lt;p data-end=&quot;5592&quot; data-start=&quot;5537&quot; data-ke-size=&quot;size16&quot;&gt;똑같은 문제가 나오면 맞힐 수 있습니다.&lt;br /&gt;하지만 숫자나 상황이 조금만 바뀌면 틀릴 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5608&quot; data-start=&quot;5594&quot; data-ke-size=&quot;size16&quot;&gt;결정트리도 마찬가지입니다.&lt;/p&gt;
&lt;p data-end=&quot;5651&quot; data-start=&quot;5610&quot; data-ke-size=&quot;size16&quot;&gt;학습 데이터에만 너무 딱 맞게 자라면 새로운 데이터에 약해질 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5691&quot; data-start=&quot;5653&quot; data-ke-size=&quot;size16&quot;&gt;그래서 너무 복잡한 가지를 줄이고 핵심만 남기는 것이 가지치기입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (5).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2hPcl/dJMcaakM0Pf/5lcc5VW0IwinMbeepwr9WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2hPcl/dJMcaakM0Pf/5lcc5VW0IwinMbeepwr9WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2hPcl/dJMcaakM0Pf/5lcc5VW0IwinMbeepwr9WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2hPcl%2FdJMcaakM0Pf%2F5lcc5VW0IwinMbeepwr9WK%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;1672&quot; height=&quot;941&quot; data-filename=&quot;ChatGPT Image 2026년 4월 28일 오전 10_08_34 (5).png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;5805&quot; data-start=&quot;5776&quot; data-ke-size=&quot;size16&quot;&gt;위 그림처럼 가지치기 전에는 나무가 너무 복잡합니다.&lt;/p&gt;
&lt;p data-end=&quot;5830&quot; data-start=&quot;5807&quot; data-ke-size=&quot;size16&quot;&gt;질문이 너무 많고, 가지가 너무 많습니다.&lt;/p&gt;
&lt;p data-end=&quot;5857&quot; data-start=&quot;5832&quot; data-ke-size=&quot;size16&quot;&gt;하지만 가지치기 후에는 핵심 질문만 남습니다.&lt;/p&gt;
&lt;p data-end=&quot;5885&quot; data-start=&quot;5859&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 과적합을 줄이는 데 도움이 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;5939&quot; data-start=&quot;5887&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트에서는 여러 트리를 함께 사용하기 때문에 결정트리 하나보다 과적합에 강한 편입니다.&lt;/p&gt;
&lt;p data-end=&quot;5984&quot; data-start=&quot;5941&quot; data-ke-size=&quot;size16&quot;&gt;하지만 그래도 트리가 너무 복잡해지지 않도록 여러 설정을 조절할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6005&quot; data-start=&quot;5986&quot; data-ke-size=&quot;size16&quot;&gt;대표적으로 이런 설정들이 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;max_depth&lt;/span&gt;&lt;br /&gt;&lt;span&gt;min_samples_split&lt;/span&gt;&lt;br /&gt;&lt;span&gt;min_samples_leaf&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;6110&quot; data-start=&quot;6065&quot; data-ke-size=&quot;size16&quot;&gt;여기서 max_depth는 트리가 너무 깊게 자라지 않도록 제한하는 값입니다.&lt;/p&gt;
&lt;p data-end=&quot;6165&quot; data-start=&quot;6112&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 max_depth=5라고 하면 트리의 깊이를 5단계 정도로 제한하겠다는 뜻입니다.&lt;/p&gt;
&lt;hr data-end=&quot;6170&quot; data-start=&quot;6167&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6188&quot; data-start=&quot;6172&quot; data-section-id=&quot;h6ec7y&quot; data-ke-size=&quot;size26&quot;&gt;8. 랜덤포레스트의 장점&lt;/h2&gt;
&lt;p data-end=&quot;6224&quot; data-start=&quot;6190&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 실무에서도 많이 쓰이는 머신러닝 알고리즘입니다.&lt;/p&gt;
&lt;p data-end=&quot;6245&quot; data-start=&quot;6226&quot; data-ke-size=&quot;size16&quot;&gt;이유는 장점이 꽤 많기 때문입니다.&lt;/p&gt;
&lt;p data-end=&quot;6276&quot; data-start=&quot;6247&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 장점은 &lt;b&gt;성능이 안정적&lt;/b&gt;이라는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;6371&quot; data-start=&quot;6278&quot; data-ke-size=&quot;size16&quot;&gt;하나의 결정트리만 사용하면 특정 데이터에 너무 치우칠 수 있습니다.&lt;br /&gt;하지만 랜덤포레스트는 여러 결정트리의 의견을 모으기 때문에 더 안정적인 판단을 할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6405&quot; data-start=&quot;6373&quot; data-ke-size=&quot;size16&quot;&gt;두 번째 장점은 &lt;b&gt;과적합에 비교적 강하다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;6484&quot; data-start=&quot;6407&quot; data-ke-size=&quot;size16&quot;&gt;여러 나무가 서로 다른 데이터를 보고 공부하기 때문에 하나의 나무가 잘못된 방향으로 판단해도 전체 결과가 크게 흔들리지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6525&quot; data-start=&quot;6486&quot; data-ke-size=&quot;size16&quot;&gt;세 번째 장점은 &lt;b&gt;분류와 회귀 모두에 사용할 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;6545&quot; data-start=&quot;6527&quot; data-ke-size=&quot;size16&quot;&gt;분류는 종류를 맞히는 문제입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;스팸 메일인가?&lt;/span&gt;&lt;br /&gt;&lt;span&gt;고양이인가 강아지인가?&lt;/span&gt;&lt;br /&gt;&lt;span&gt;합격인가 불합격인가?&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;6613&quot; data-start=&quot;6594&quot; data-ke-size=&quot;size16&quot;&gt;회귀는 숫자를 예측하는 문제입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;집값은 얼마일까?&lt;/span&gt;&lt;br /&gt;&lt;span&gt;내일 온도는 몇 도일까?&lt;/span&gt;&lt;br /&gt;&lt;span&gt;상품 판매량은 얼마나 될까?&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;6698&quot; data-start=&quot;6668&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 이런 문제들에 모두 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6740&quot; data-start=&quot;6700&quot; data-ke-size=&quot;size16&quot;&gt;네 번째 장점은 &lt;b&gt;어떤 특징이 중요한지 확인할 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;6782&quot; data-start=&quot;6742&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 집값을 예측할 때 어떤 요소가 중요했는지 확인할 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;위치&lt;/span&gt;&lt;br /&gt;&lt;span&gt;면적&lt;/span&gt;&lt;br /&gt;&lt;span&gt;방 개수&lt;/span&gt;&lt;br /&gt;&lt;span&gt;층수&lt;/span&gt;&lt;br /&gt;&lt;span&gt;건축 연도&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;6854&quot; data-start=&quot;6817&quot; data-ke-size=&quot;size16&quot;&gt;이 중 어떤 특징이 예측에 더 많이 영향을 줬는지 볼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6901&quot; data-start=&quot;6856&quot; data-ke-size=&quot;size16&quot;&gt;이것을 보통 &lt;b&gt;특성 중요도(Feature Importance)&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;hr data-end=&quot;6906&quot; data-start=&quot;6903&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6924&quot; data-start=&quot;6908&quot; data-section-id=&quot;1o1jtma&quot; data-ke-size=&quot;size26&quot;&gt;9. 랜덤포레스트의 단점&lt;/h2&gt;
&lt;p data-end=&quot;6952&quot; data-start=&quot;6926&quot; data-ke-size=&quot;size16&quot;&gt;물론 랜덤포레스트도 완벽한 알고리즘은 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;6963&quot; data-start=&quot;6954&quot; data-ke-size=&quot;size16&quot;&gt;단점도 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;7000&quot; data-start=&quot;6965&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 단점은 &lt;b&gt;계산 시간이 늘어날 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;7053&quot; data-start=&quot;7002&quot; data-ke-size=&quot;size16&quot;&gt;결정트리 하나만 만드는 것보다 결정트리 100개, 200개를 만드는 것이 더 오래 걸립니다.&lt;/p&gt;
&lt;p data-end=&quot;7099&quot; data-start=&quot;7055&quot; data-ke-size=&quot;size16&quot;&gt;그래서 데이터가 아주 크거나 빠른 응답이 필요한 서비스에서는 조정이 필요합니다.&lt;/p&gt;
&lt;p data-end=&quot;7142&quot; data-start=&quot;7101&quot; data-ke-size=&quot;size16&quot;&gt;두 번째 단점은 &lt;b&gt;결과를 설명하기가 조금 어려울 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;7175&quot; data-start=&quot;7144&quot; data-ke-size=&quot;size16&quot;&gt;결정트리 하나는 질문 흐름을 따라가면 이해하기 쉽습니다.&lt;/p&gt;
&lt;p data-end=&quot;7241&quot; data-start=&quot;7177&quot; data-ke-size=&quot;size16&quot;&gt;하지만 랜덤포레스트는 수많은 결정트리의 결과를 합칩니다.&lt;br /&gt;그래서 전체 판단 과정을 한눈에 설명하기는 어렵습니다.&lt;/p&gt;
&lt;p data-end=&quot;7277&quot; data-start=&quot;7243&quot; data-ke-size=&quot;size16&quot;&gt;세 번째 단점은 &lt;b&gt;모델 크기가 커질 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-end=&quot;7352&quot; data-start=&quot;7279&quot; data-ke-size=&quot;size16&quot;&gt;n_estimators 값을 크게 하면 나무가 많아집니다.&lt;br /&gt;나무가 많아지면 모델도 커지고, 예측 시간도 늘어날 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;7357&quot; data-start=&quot;7354&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7381&quot; data-start=&quot;7359&quot; data-section-id=&quot;1b51yi2&quot; data-ke-size=&quot;size26&quot;&gt;10. 주요 용어 한 번에 정리하기&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;용어쉬운 설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;7722&quot; data-start=&quot;7383&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;7722&quot; data-start=&quot;7408&quot;&gt;
&lt;tr data-end=&quot;7439&quot; data-start=&quot;7408&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7415&quot; data-start=&quot;7408&quot;&gt;결정트리&lt;/td&gt;
&lt;td data-end=&quot;7439&quot; data-start=&quot;7415&quot; data-col-size=&quot;sm&quot;&gt;질문을 따라가며 답을 찾는 나무 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7475&quot; data-start=&quot;7440&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7449&quot; data-start=&quot;7440&quot;&gt;랜덤포레스트&lt;/td&gt;
&lt;td data-end=&quot;7475&quot; data-start=&quot;7449&quot; data-col-size=&quot;sm&quot;&gt;여러 결정트리를 모아 만든 머신러닝 모델&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7514&quot; data-start=&quot;7476&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7482&quot; data-start=&quot;7476&quot;&gt;앙상블&lt;/td&gt;
&lt;td data-end=&quot;7514&quot; data-start=&quot;7482&quot; data-col-size=&quot;sm&quot;&gt;여러 모델의 힘을 합쳐 더 좋은 결과를 만드는 방법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7557&quot; data-start=&quot;7515&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7520&quot; data-start=&quot;7515&quot;&gt;배깅&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7557&quot; data-start=&quot;7520&quot;&gt;데이터를 랜덤하게 여러 번 뽑아 여러 모델을 학습시키는 방법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7595&quot; data-start=&quot;7558&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7565&quot; data-start=&quot;7558&quot;&gt;가지치기&lt;/td&gt;
&lt;td data-end=&quot;7595&quot; data-start=&quot;7565&quot; data-col-size=&quot;sm&quot;&gt;너무 복잡한 트리의 불필요한 가지를 줄이는 방법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7637&quot; data-start=&quot;7596&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7611&quot; data-start=&quot;7596&quot;&gt;n_estimators&lt;/td&gt;
&lt;td data-end=&quot;7637&quot; data-start=&quot;7611&quot; data-col-size=&quot;sm&quot;&gt;랜덤포레스트 안에 들어가는 결정트리 개수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7678&quot; data-start=&quot;7638&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7644&quot; data-start=&quot;7638&quot;&gt;과적합&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7678&quot; data-start=&quot;7644&quot;&gt;학습 데이터는 잘 맞히지만 새로운 데이터에는 약한 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7722&quot; data-start=&quot;7679&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7691&quot; data-start=&quot;7679&quot;&gt;max_depth&lt;/td&gt;
&lt;td data-end=&quot;7722&quot; data-start=&quot;7691&quot; data-col-size=&quot;sm&quot;&gt;결정트리가 얼마나 깊게 자랄 수 있는지 정하는 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;7727&quot; data-start=&quot;7724&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7750&quot; data-start=&quot;7729&quot; data-section-id=&quot;m146vz&quot; data-ke-size=&quot;size26&quot;&gt;11. 간단한 파이썬 코드로 보기&lt;/h2&gt;
&lt;p data-end=&quot;7807&quot; data-start=&quot;7752&quot; data-ke-size=&quot;size16&quot;&gt;파이썬에서는 scikit-learn 라이브러리를 사용하면 랜덤포레스트를 쉽게 만들 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777339073441&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(
n_estimators=100,
max_depth=5,
random_state=42
)

model.fit(X_train, y_train)

pred = model.predict(X_test)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8050&quot; data-start=&quot;8029&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777339080928&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n_estimators=100&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8107&quot; data-start=&quot;8084&quot; data-ke-size=&quot;size16&quot;&gt;결정트리 100개를 사용하겠다는 뜻입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777339086424&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;max_depth=5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8169&quot; data-start=&quot;8136&quot; data-ke-size=&quot;size16&quot;&gt;각 결정트리가 너무 깊게 자라지 않도록 제한한다는 뜻입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777339093393&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;random_state=42&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8249&quot; data-start=&quot;8202&quot; data-ke-size=&quot;size16&quot;&gt;랜덤하게 뽑는 과정을 고정해서, 다시 실행해도 같은 결과가 나오도록 하는 설정입니다.&lt;/p&gt;
&lt;p data-end=&quot;8278&quot; data-start=&quot;8251&quot; data-ke-size=&quot;size16&quot;&gt;처음 공부할 때는 이 정도만 이해해도 충분합니다.&lt;/p&gt;
&lt;hr data-end=&quot;8283&quot; data-start=&quot;8280&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8312&quot; data-start=&quot;8285&quot; data-section-id=&quot;6r7vtr&quot; data-ke-size=&quot;size26&quot;&gt;12. 랜덤포레스트를 한 문장으로 정리하면?&lt;/h2&gt;
&lt;p data-end=&quot;8337&quot; data-start=&quot;8314&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 이렇게 정리할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;8396&quot; data-start=&quot;8339&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;8396&quot; data-start=&quot;8341&quot; data-ke-size=&quot;size16&quot;&gt;여러 개의 결정트리를 만들고, 각각의 의견을 모아서 더 안정적인 결정을 내리는 머신러닝 방법입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;8429&quot; data-start=&quot;8398&quot; data-ke-size=&quot;size16&quot;&gt;초등학생식으로 더 쉽게 말하면 이렇게 말할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;8482&quot; data-start=&quot;8431&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;8482&quot; data-start=&quot;8433&quot; data-ke-size=&quot;size16&quot;&gt;한 명에게만 물어보지 않고, 여러 명에게 물어본 다음 다수결로 정하는 똑똑한 방법입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;8512&quot; data-start=&quot;8484&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 이름은 어렵지만, 핵심은 단순합니다.&lt;/p&gt;
&lt;p data-end=&quot;8578&quot; data-start=&quot;8514&quot; data-ke-size=&quot;size16&quot;&gt;나무 하나보다 숲이 더 튼튼하듯이, 모델 하나보다 여러 모델이 함께 판단하면 더 안정적인 결과를 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;8583&quot; data-start=&quot;8580&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8591&quot; data-start=&quot;8585&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;8634&quot; data-start=&quot;8593&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트는 머신러닝을 처음 공부할 때 꼭 알아두면 좋은 알고리즘입니다.&lt;/p&gt;
&lt;p data-end=&quot;8699&quot; data-start=&quot;8636&quot; data-ke-size=&quot;size16&quot;&gt;결정트리, 앙상블, 배깅, 가지치기, n_estimators 같은 용어가 처음에는 어렵게 느껴질 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;8754&quot; data-start=&quot;8701&quot; data-ke-size=&quot;size16&quot;&gt;하지만 숲속의 여러 나무가 함께 의견을 내고 투표한다고 생각하면 훨씬 쉽게 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;8770&quot; data-start=&quot;8756&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;결정트리 = 질문을 따라 답을 찾는 나무&lt;/span&gt;&lt;br /&gt;&lt;span&gt;랜덤포레스트 = 여러 결정트리가 모인 숲&lt;/span&gt;&lt;br /&gt;&lt;span&gt;앙상블 = 여러 모델의 힘을 합치는 방법&lt;/span&gt;&lt;br /&gt;&lt;span&gt;배깅 = 데이터를 랜덤하게 나눠 여러 모델을 학습시키는 방법&lt;/span&gt;&lt;br /&gt;&lt;span&gt;n_estimators = 숲 안에 있는 나무의 개수&lt;/span&gt;&lt;br /&gt;&lt;span&gt;가지치기 = 너무 복잡한 나무를 깔끔하게 정리하는 방법&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;8989&quot; data-start=&quot;8949&quot; data-ke-size=&quot;size16&quot;&gt;랜덤포레스트를 이해하면 머신러닝의 중요한 흐름 하나를 잡을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;9050&quot; data-start=&quot;8991&quot; data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;하나의 모델에만 의존하지 않고, 여러 모델의 의견을 모아 더 좋은 결정을 내리는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;9104&quot; data-start=&quot;9052&quot; data-ke-size=&quot;size16&quot;&gt;이 개념은 랜덤포레스트뿐만 아니라 다른 여러 머신러닝 기법을 이해하는 데도 큰 도움이 됩니다.&lt;/p&gt;</description>
      <category>AI/머신러닝</category>
      <category>n_estimators</category>
      <category>randomforest</category>
      <category>가지치기</category>
      <category>결정트리</category>
      <category>랜덤포레스트</category>
      <category>머신러닝</category>
      <category>배깅</category>
      <category>앙상블</category>
      <category>초보자머신러닝</category>
      <category>파이썬머신러닝</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/59</guid>
      <comments>https://threemealsofcoding.tistory.com/59#entry59comment</comments>
      <pubDate>Tue, 28 Apr 2026 10:15:04 +0900</pubDate>
    </item>
    <item>
      <title>숫자의 함정에서 벗어나는 법: 스케일링(Scaling)과 로그(Log) 변환</title>
      <link>https://threemealsofcoding.tistory.com/58</link>
      <description>&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;데이터 분석이나 머신러닝 공부를 시작하면 가장 먼저 마주치는 벽이 있습니다. 바로 &quot;데이터 전처리&quot;죠. 그중에서도 &quot;왜 멀쩡한 숫자에 로그를 취하나요?&quot;, &quot;스케일링은 왜 꼭 해야 하나요?&quot;라는 질문에 대한 답을 오늘 완벽하게 정리해 보겠습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;4&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;1. 로그(Log) 변환: &quot;폭발적인 숫자를 잠재우는 마법&quot;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;머신러닝 모델은 '숫자의 크기'에 굉장히 민감합니다. 하지만 우리가 사는 세상의 데이터는 그리 고르지 않습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;  집값 데이터의 예시&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;대부분의 집은 3억~10억 사이에 몰려 있지만, 가끔 100억이 넘는 초고가 펜트하우스가 등장합니다. 이를 데이터 시각화해보면 왼쪽으로 치우치고 오른쪽 꼬리가 아주 긴(Right-Skewed) 모양이 됩니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;로그를 왜 취하나요?&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;데이터의 격차 해소&lt;/b&gt;: 로그는 큰 숫자를 작게 만들고, 작은 숫자들 사이의 간격은 상대적으로 넓게 만듭니다. 100억이라는 거대한 숫자를 로그의 세계로 가져오면 다른 데이터들과 '대화가 가능한 수준'으로 차분해집니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;종 모양(정규분포)으로 만들기&lt;/b&gt;: 머신러닝 모델(특히 선형 회귀)은 데이터가 예쁜 종 모양일 때 학습 성능이 가장 좋습니다. 로그는 삐져나온 꼬리를 안으로 당겨 데이터를 종 모양에 가깝게 펴주는 역할을 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;비율(%)의 관점&lt;/b&gt;: 1억에서 2억이 되는 것(2배)과 10억에서 20억이 되는 것(2배)을 동일한 '성장'으로 인식하게 합니다. 이는 복리나 성장률이 중요한 금융 데이터에서 필수적입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-path-to-node=&quot;11&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;11,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0&quot;&gt;  꿀팁&lt;/b&gt;: 데이터에 0이 있으면 로그값은 정의되지 않습니다(마이너스 무한대). 그래서 보통 log(x + 1)을 해주는 &lt;b data-index-in-node=&quot;68&quot; data-path-to-node=&quot;11,0&quot;&gt;np.log1p()&lt;/b&gt; 함수를 사용합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-path-to-node=&quot;12&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;2. 데이터 스케일링(Scaling): &quot;공평한 운동장을 만드는 과정&quot;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;스케일링은 한마디로 &quot;단위(Unit)의 통일&quot;입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt; &amp;zwj;♂️ 유치원생과 거인의 달리기 시합&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;데이터셋에 '방의 개수(1~5개)'와 '거실 면적(100~3000sqft)'이라는 두 변수가 있다고 가정해 봅시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방이 1개 늘어나는 것은 엄청난 변화지만, 수치상으로는 겨우 '+1'입니다.&lt;/li&gt;
&lt;li&gt;면적이 조금 넓어지는 것은 사소할 수 있지만, 수치상으로는 '+100'이 훌쩍 넘습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;모델은 바보같이 &quot;오! 숫자가 큰 면적이 훨씬 중요한 변수구나!&quot;라고 오해하게 됩니다. 이를 막기 위해 모든 변수를 동일한 체급으로 맞춰주는 것이 스케일링입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;대표적인 스케일링 3대장&lt;/h3&gt;
&lt;h4 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size20&quot;&gt;① StandardScaler (표준화)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;21&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,0,0&quot;&gt;방식&lt;/b&gt;: 평균을 0, 표준편차를 1로 만듭니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,1,0&quot;&gt;특징&lt;/b&gt;: 데이터를 '평균 근처에 얼마나 모여 있는가'로 재배치합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,2,0&quot;&gt;단점&lt;/b&gt;: 이상치(Outlier)가 있으면 평균이 확 변해서 성능이 떨어질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size20&quot;&gt;② MinMaxScaler (정규화)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0,0&quot;&gt;방식&lt;/b&gt;: 최솟값을 0, 최댓값을 1로 압축합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,1,0&quot;&gt;특징&lt;/b&gt;: 모든 데이터를 0과 1 사이의 좁은 감옥에 가둡니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,2,0&quot;&gt;단점&lt;/b&gt;: 이상치가 '1억'처럼 크다면, 나머지 데이터들은 0.0001 근처에 닥닥 붙게 되어 변별력을 잃습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size20&quot;&gt;③ RobustScaler (이상치 해결사)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;25&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,0,0&quot;&gt;방식&lt;/b&gt;: 평균 대신 중앙값(Median)과 사분위수(IQR)를 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,1,0&quot;&gt;특징&lt;/b&gt;: &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;25,1,0&quot;&gt;이상치에 매우 강합니다.&lt;/b&gt; 튀는 값이 한두 개 있어도 대다수 데이터의 체급을 아주 예쁘게 잘 맞춰줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,2,0&quot;&gt;결론&lt;/b&gt;: 실제 현업 데이터나 캐글 집값 데이터처럼 튀는 값이 많은 경우 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;25,2,0&quot;&gt;가장 정석적인 선택&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;27&quot; data-ke-size=&quot;size26&quot;&gt;  블로그 요약 노트&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;28&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;28,0,0&quot;&gt;로그 변환&lt;/b&gt;은 데이터의 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;28,0,0&quot;&gt;분포&lt;/b&gt;를 고르게 만들고(정규화), &lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;28,0,0&quot;&gt;비율&lt;/b&gt;의 관점을 도입하기 위해 쓴다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;28,1,0&quot;&gt;스케일링&lt;/b&gt;은 서로 다른 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;28,1,0&quot;&gt;단위&lt;/b&gt;를 가진 변수들을 공평하게 비교하기 위해 필수적이다.&lt;/li&gt;
&lt;li&gt;이상치가 걱정된다면 고민 말고 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;28,2,0&quot;&gt;RobustScaler&lt;/b&gt;를 선택하자.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>AI/머신러닝</category>
      <category>python데이터분석</category>
      <category>RobustScaler</category>
      <category>데이터스케일링</category>
      <category>데이터전처리</category>
      <category>로그변환</category>
      <category>머신러닝기초</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/58</guid>
      <comments>https://threemealsofcoding.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 28 Apr 2026 09:55:14 +0900</pubDate>
    </item>
    <item>
      <title>Nginx로 HTTPS(SSL) 구축하기 (Reverse Proxy + Let&amp;rsquo;s Encrypt)</title>
      <link>https://threemealsofcoding.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 HTTPS를 붙인다고 하면 꽤 부담스러운 작업처럼 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 발급, CSR 생성, 인증서 파일 적용, 웹서버 설정, 갱신 관리까지 손이 많이 갔고, 작은 서비스 하나 올리는데도 진입장벽이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Nginx + Let&amp;rsquo;s Encrypt + Certbot 조합으로 구성해보니 생각보다 훨씬 간단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 개인 서버나 소규모 서비스에서는 &lt;b&gt;&amp;ldquo;도메인 연결 + Nginx reverse proxy + certbot 실행&amp;rdquo;&lt;/b&gt; 정도만으로도 HTTPS 서비스를 빠르게 올릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Nginx를 이용해 SSL(HTTPS)을 붙이는 전체 흐름&lt;/b&gt;을 실제 적용 순서대로 정리한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 구성 목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 목표로 한 형태는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인: example.com (본인 도메인으로 변경)&lt;/li&gt;
&lt;li&gt;웹서버: nginx&lt;/li&gt;
&lt;li&gt;내부 서비스: 웹 애플리케이션 (예: 127.0.0.1:3000)&lt;/li&gt;
&lt;li&gt;외부 접속: &lt;a href=&quot;https://example.com&quot;&gt;https://example.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;방식: Nginx가 80/443 포트를 받고, 내부 서비스로 reverse proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;사용자 브라우저
    &amp;darr;
&amp;lt;https://example.com&amp;gt;
    &amp;darr;
Nginx (80/443)
    &amp;darr;
내부 서비스 (예: 127.0.0.1:3000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조의 장점은 명확하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부에는 80/443만 공개&lt;/li&gt;
&lt;li&gt;내부 서비스는 로컬 포트로만 유지 가능&lt;/li&gt;
&lt;li&gt;SSL 처리는 Nginx에서 일괄 담당&lt;/li&gt;
&lt;li&gt;서비스가 늘어나도 도메인만 추가해 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 먼저 도메인 연결부터&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS를 붙이기 전에 가장 먼저 해야 할 일은 &lt;b&gt;도메인이 서버를 정확히 가리키도록 DNS를 설정하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 example.com를 쓰려면 DNS에서 A 레코드를 등록한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트: @ 또는 서브도메인(예: app)&lt;/li&gt;
&lt;li&gt;타입: A&lt;/li&gt;
&lt;li&gt;값: 서버 공인 IP&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 처음에는 짧게 두면 변경 반영 여부를 확인하기 편하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Nginx reverse proxy 먼저 구성 (HTTP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSL을 붙이기 전에 먼저 &lt;b&gt;HTTP로 서비스가 정상 연결되는지&lt;/b&gt; 확인하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 HTTPS까지 한 번에 보려고 하면 DNS 문제인지, Nginx 문제인지, 애플리케이션 문제인지 구분이 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 가장 단순한 reverse proxy 예시다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;

    server_name example.com;

    location / {
        proxy_pass &amp;lt;http://127.0.0.1:3000&amp;gt;;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 등 업그레이드가 필요한 서비스라면 아래 헤더가 필요할 수 있음
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &quot;upgrade&quot;;

        proxy_buffering off;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 후에는 아래처럼 테스트한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo nginx -t
sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 브라우저에서 접속해본다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;&amp;lt;http://example.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 서비스가 정상적으로 뜨면, 그 다음이 HTTPS 단계다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;. 중간에 만난 문제: 정적 파일 404&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 reverse proxy만 걸면 끝날 줄 알았는데, 실제로는 하위 리소스(정적 파일)들이 404가 나는 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아래 같은 요청이 계속 실패했다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;&amp;lt;http://example.com/static/loader.js&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 확인해보니 &lt;b&gt;Nginx 설정에 남아 있던 정적 웹서버 설정(root 등)이 요청을 가로채고&lt;/b&gt; 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 server 블록에 아래 설정이 남아 있으면:&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;root /var/www/html;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 /static/loader.js 요청을 upstream으로 넘기지 않고 로컬 파일 경로에서 먼저 찾는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기대했던 동작: /static/loader.js &amp;rarr; upstream(내부 서비스)로 전달&lt;/li&gt;
&lt;li&gt;실제 동작: /static/loader.js &amp;rarr; Nginx 로컬 경로에서 파일 검색 &amp;rarr; 404&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 reverse proxy 전용 server 블록에서는 root, index, try_files 같은 정적 웹서버 설정을 빼고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요청을 전부 proxy_pass로 넘기는 구조&lt;/b&gt;가 더 깔끔했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Certbot으로 HTTPS 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP가 정상이라면 이제 SSL 인증서를 붙이면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 &lt;b&gt;Certbot&lt;/b&gt;이 발급과 Nginx 설정 변경까지 대부분을 자동화해준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(1) 설치 (Ubuntu 예시)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포판/환경에 따라 설치 방식이 다를 수 있으니, 공식 문서를 함께 확인하는 것을 권장한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install -y snapd
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(2) 인증서 발급 및 Nginx 설정 자동 반영&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;sudo certbot --nginx -d example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중 이메일, 약관 동의, HTTP&amp;rarr;HTTPS 리다이렉트 여부 등을 묻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리다이렉트를 선택하면 Certbot이 Nginx 설정까지 자동으로 수정해준다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 최종 구성 형태 (HTTPS + 리다이렉트)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 경로는 Certbot이 발급한 도메인 기준으로 자동 생성되며, 환경에 따라 다를 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;

    server_name example.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass &amp;lt;http://127.0.0.1:3000&amp;gt;;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &quot;upgrade&quot;;

        proxy_buffering off;
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 사용자는 &lt;a href=&quot;https://example.com&quot;&gt;https://example.com&lt;/a&gt;로 접속하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx가 SSL을 종료한 뒤 내부 서비스로 안전하게 전달한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 자동 갱신 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSL 구축이 쉬워진 가장 큰 이유는 &lt;b&gt;갱신 관리까지 자동화가 가능&lt;/b&gt;하다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 dry-run으로 갱신 테스트를 해볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;sudo certbot renew --dry-run
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 성공하면 운영 중 인증서 만료로 인한 사고 가능성이 크게 줄어든다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 여러 서비스로 확장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 한 서비스에서 끝나지 않고, 서비스가 늘어나도 쉽게 확장할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;app.example.com &amp;rarr; 웹 서비스&lt;/li&gt;
&lt;li&gt;api.example.com &amp;rarr; API 서버&lt;/li&gt;
&lt;li&gt;admin.example.com &amp;rarr; 관리자 페이지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Nginx를 진입점으로 두고, 뒤쪽 서비스들을 reverse proxy로 나누면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소규모 환경에서는 &lt;b&gt;호스트에 Nginx를 설치해 SSL과 라우팅을 담당하게 하는 방식&lt;/b&gt;이 특히 실용적이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 흐름은 단순하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;도메인을 서버 IP에 연결&lt;/li&gt;
&lt;li&gt;Nginx reverse proxy로 내부 서비스 연결&lt;/li&gt;
&lt;li&gt;HTTP로 정상 동작 확인&lt;/li&gt;
&lt;li&gt;Certbot으로 SSL 적용&lt;/li&gt;
&lt;li&gt;HTTPS 리다이렉트 및 자동 갱신 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 SSL이 어렵게 느껴졌지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 오히려 &lt;b&gt;기본 인프라 작업 중 가장 빠르게 끝나는 편&lt;/b&gt;에 가까워졌다.&lt;/p&gt;</description>
      <category>WEB_WAS/Nginx</category>
      <category>certbot</category>
      <category>docker</category>
      <category>HTTPS</category>
      <category>letsencrypt</category>
      <category>Linux</category>
      <category>Nginx</category>
      <category>openwebui</category>
      <category>reverseproxy</category>
      <category>SSL</category>
      <category>ubuntu</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/57</guid>
      <comments>https://threemealsofcoding.tistory.com/57#entry57comment</comments>
      <pubDate>Fri, 20 Mar 2026 11:17:31 +0900</pubDate>
    </item>
    <item>
      <title>클라우드 없이 로컬 LLM 환경 구축하기 (Ubuntu + Ollama + Open WebUI, GTX 1080 테스트)</title>
      <link>https://threemealsofcoding.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 LLM을 서비스나 개인 프로젝트에 붙이려면 결국 &lt;b&gt;비용 문제&lt;/b&gt;가 따라옵니다. 처음에는 &amp;ldquo;API로 일단 써보자&amp;rdquo;로 시작하지만, 프롬프트를 조금만 바꾸고 결과를 비교하는 과정을 반복하면 호출량이 금방 쌓입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 방향을 바꿔 &lt;b&gt;클라우드 없이 Ubuntu 서버에 로컬 LLM 환경을 직접 구성&lt;/b&gt;해봤습니다. 특히 집에 남는 **GTX 1080 장비를 재활용해서 &amp;lsquo;가볍게 테스트&amp;rsquo;**해보는 게 목적이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; 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;Ubuntu에 Ollama 설치&lt;/li&gt;
&lt;li&gt;모델 1개 다운로드 후 실행&lt;/li&gt;
&lt;li&gt;Open WebUI까지 붙여 브라우저에서 테스트&lt;/li&gt;
&lt;li&gt;GTX 1080 환경에서 &amp;ldquo;쓸 만한지&amp;rdquo; 가볍게 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 로컬 LLM을 써보려고 했나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 LLM을 보게 된 이유는 단순히 &lt;b&gt;비용 통제&lt;/b&gt; 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 테스트하려는 방향은 자유 채팅형 서비스라기보다, 정해진 데이터를 넣고 요약하거나 설명을 만들어내는 &lt;b&gt;내부 분석형 구조&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 작업입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;412&quot; data-start=&quot;354&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;361&quot; data-start=&quot;354&quot; data-section-id=&quot;mvfz2d&quot;&gt;문서 요약&lt;/li&gt;
&lt;li data-end=&quot;373&quot; data-start=&quot;362&quot; data-section-id=&quot;iww83o&quot;&gt;핵심 포인트 정리&lt;/li&gt;
&lt;li data-end=&quot;391&quot; data-start=&quot;374&quot; data-section-id=&quot;1bsh2vt&quot;&gt;쉬운 표현으로 다시 설명하기&lt;/li&gt;
&lt;li data-end=&quot;412&quot; data-start=&quot;392&quot; data-section-id=&quot;1ehnoit&quot;&gt;입력 데이터 기반 요약 문구 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 꼭 비싼 클라우드 모델이 아니어도 어느 정도 처리할 수 있습니다.&lt;br /&gt;오히려 중요한 건 아래에 더 가까웠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;543&quot; data-start=&quot;485&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;505&quot; data-start=&quot;485&quot; data-section-id=&quot;1l2kjc9&quot;&gt;내가 가진 데이터를 잘 정리하느냐&lt;/li&gt;
&lt;li data-end=&quot;526&quot; data-start=&quot;506&quot; data-section-id=&quot;fhlpl&quot;&gt;응답 속도가 감당 가능한 수준이냐&lt;/li&gt;
&lt;li data-end=&quot;543&quot; data-start=&quot;527&quot; data-section-id=&quot;hq1524&quot;&gt;운영비를 통제할 수 있느냐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 &lt;b&gt;&amp;ldquo;최고 성능 모델&amp;rdquo;보다 &amp;ldquo;운영 가능한 로컬 환경&amp;rdquo;&lt;/b&gt; 쪽에 초점을 맞췄습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ollama는 정확히 뭘까&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRRUZf/dJMcajaqsy0/GKQc0EKjipt3bR3wu33mfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRRUZf/dJMcajaqsy0/GKQc0EKjipt3bR3wu33mfk/img.png&quot; data-alt=&quot;Ollama 홈페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRRUZf/dJMcajaqsy0/GKQc0EKjipt3bR3wu33mfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRRUZf%2FdJMcajaqsy0%2FGKQc0EKjipt3bR3wu33mfk%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;1892&quot; height=&quot;633&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ollama 홈페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 헷갈리기 쉬운데 &lt;b&gt;Ollama는 모델 자체가 아닙니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 아래처럼 이해하는 게 가장 편합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Qwen, Llama, EXAONE &amp;rarr; 실제 모델&lt;/li&gt;
&lt;li&gt;Ollama &amp;rarr; 로컬에서 모델을 실행하게 해주는 런타임(실행 도구)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Ollama를 설치한다고 바로 AI가 완성되는 것은 아니고, 설치 후에 &lt;b&gt;어떤 모델을 쓸지 골라서 다운로드&lt;/b&gt;해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ollama run qwen2.5:7b
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 장비면 테스트가 가능할까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 RTX 4090이나 서버급 GPU가 꼭 필요한 건 아닙니다. 가볍게 테스트하는 수준이라면 문턱이 생각보다 낮습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 가볍게 설치/동작 확인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GTX 1080&lt;/li&gt;
&lt;li&gt;RAM 16GB 이상&lt;/li&gt;
&lt;li&gt;7B급 경량 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도면 &amp;ldquo;설치가 되는지&amp;rdquo;, &amp;ldquo;모델이 뜨는지&amp;rdquo;, &amp;ldquo;요약이 어느 정도 되는지&amp;rdquo;는 충분히 확인할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DwDWH/dJMcagrgNeX/tqGsM0e1FcG5CdSAxI5kr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DwDWH/dJMcagrgNeX/tqGsM0e1FcG5CdSAxI5kr0/img.png&quot; data-alt=&quot;nvidia-smi 실행 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DwDWH/dJMcagrgNeX/tqGsM0e1FcG5CdSAxI5kr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwDWH%2FdJMcagrgNeX%2FtqGsM0e1FcG5CdSAxI5kr0%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;844&quot; height=&quot;395&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nvidia-smi 실행 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 조금 더 무난하게 써보기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTX 4070 SUPER / RTX 5070&lt;/li&gt;
&lt;li&gt;RAM 32GB 이상&lt;/li&gt;
&lt;li&gt;7B~8B급 모델 운영&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 넘어서 로컬 실사용도 현실적인 구간입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 여유 있게 실험해보기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VRAM 16GB 이상&lt;/li&gt;
&lt;li&gt;14B급 모델까지 검토 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제 체감은 모델 크기, 컨텍스트 길이, 동시 요청 수에 따라 달라집니다. 다만 출발점으로는 &lt;b&gt;GTX 1080 + 7B급&lt;/b&gt;도 충분히 의미가 있었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ubuntu에 Ollama 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 자체는 단순했습니다. 공식 설치 스크립트를 그대로 실행하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;curl -fsSL &amp;lt;https://ollama.com/install.sh&amp;gt; | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 버전 확인:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ollama -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU가 Ubuntu에서 정상적으로 잡히는지도 확인했습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 크게 어렵지 않았습니다. 진짜 시간은 &amp;ldquo;설치&amp;rdquo;보다 &lt;b&gt;연결해서 실제로 쓰는 과정&lt;/b&gt;에서 더 들었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모델은 설치 후 따로 받아야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ollama는 실행기이기 때문에, 설치 후 바로 응답이 오지 않습니다. 실제로 쓸 모델을 하나 받아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 경량 테스트용으로 Qwen 계열을 받아봤습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ollama pull qwen2.5:7b
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치된 모델 확인:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;ollama list
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 실행:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ollama run qwen2.5:7b
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 느낀 점이 하나 있었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로컬 모델은 최신 데이터를 알고 있는 모델이 아니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 IPO 관련 질문을 던졌을 때 예전 시점 정보처럼 보이는 답이 나오기도 했고, Qwen 계열은 중국어가 섞이는 경우도 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지점에서 &amp;ldquo;로컬 LLM은 최신 사실 기억용이 아니라 &lt;b&gt;내가 넣어준 데이터를 정리하는 엔진&lt;/b&gt;으로 봐야겠구나&amp;rdquo;라는 생각이 확실해졌습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저에서 테스트하려고 Open WebUI도 붙였다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서만 테스트하는 것도 가능하지만, 브라우저에서 ChatGPT처럼 확인할 수 있는 UI가 있으면 훨씬 편합니다. 그래서 Open WebUI를 같이 붙였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose 설정은 대략 아래처럼 구성했습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: always
    ports:
      - &quot;3000:8080&quot;
    environment:
      OLLAMA_BASE_URL: &quot;&amp;lt;http://192.168.0.3:11434&amp;gt;&quot;
      WEBUI_SECRET_KEY: &quot;(원하는 값으로 변경)&quot;
    volumes:
      - open-webui:/app/backend/data

volumes:
  open-webui:
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속하면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http://서버IP:3000&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-end=&quot;3475&quot; data-start=&quot;3464&quot; data-section-id=&quot;rtncch&quot; data-ke-size=&quot;size26&quot;&gt;실제 접속 화면&lt;/h2&gt;
&lt;p data-end=&quot;3531&quot; data-start=&quot;3477&quot; data-ke-size=&quot;size16&quot;&gt;아래는 실제로 Ubuntu 서버에 올린 Open WebUI에 브라우저로 접속한 화면입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1905&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhB1yK/dJMcaaYSZqC/Hi3MJTOBw8zcr36NHLKFEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhB1yK/dJMcaaYSZqC/Hi3MJTOBw8zcr36NHLKFEk/img.png&quot; data-alt=&quot;open webui 로그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhB1yK/dJMcaaYSZqC/Hi3MJTOBw8zcr36NHLKFEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhB1yK%2FdJMcaaYSZqC%2FHi3MJTOBw8zcr36NHLKFEk%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;1905&quot; height=&quot;675&quot; data-origin-width=&quot;1905&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;open webui 로그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3531&quot; data-start=&quot;3477&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;image-71d3f363-b5cd-400f-841b-b3d96cc3e030&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div data-testid=&quot;sandbox-image-actions&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-testid=&quot;sandbox-image-right-actions&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3730&quot; data-start=&quot;3619&quot; data-ke-size=&quot;size16&quot;&gt;브라우저에서도 기본적인 채팅 UI가 정상적으로 열리는 것을 확인할 수 있었고,&lt;br /&gt;&amp;ldquo;일단 로컬 LLM 환경을 웹으로 붙여서 어디서든 접속 가능한 형태로 만들 수 있겠구나&amp;rdquo; 하는 감이 왔습니다.&lt;/p&gt;
&lt;p data-end=&quot;3828&quot; data-start=&quot;3732&quot; data-ke-size=&quot;size16&quot;&gt;아직 이 단계는 완성된 서비스라기보다 테스트 환경에 가깝지만,&lt;br /&gt;Ollama와 Open WebUI 조합만으로도 생각보다 꽤 그럴듯한 로컬 AI 채팅 환경이 만들어졌습니다.&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;여기까지 오면 끝난 줄 알았는데, 실제로는 이 지점에서 Linux 특유의 네트워크 이슈를 한 번 겪었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치보다 오래 걸린 건 &amp;ldquo;연결 문제&amp;rdquo;였다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Open WebUI에서 Ollama를 바로 읽어올 줄 알았는데 연결이 안 됐습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) host.docker.internal 이슈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 등에서는 자주 쓰는 host.docker.internal 방식이 Ubuntu에서는 기본으로 바로 동작하지 않는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;호스트의 실제 내부 IP를 직접 넣는 방식&lt;/b&gt;으로 바꿨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: 192.168.0.3&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 더 큰 문제: Ollama가 127.0.0.1에만 바인딩&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결정적인 문제는 Ollama가 기본적으로 127.0.0.1:11434에만 바인딩되어 있었던 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 호스트 자신은 접근할 수 있지만 Docker 컨테이너는 접근할 수 없는 상태였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하려면 Ollama를 모든 인터페이스에 열리도록 다시 실행해야 했습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;pkill ollama
OLLAMA_HOST=0.0.0.0:11434 ollama serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Open WebUI에서 Ollama에 정상적으로 붙을 수 있었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;systemd에도 영구 반영하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트할 때는 셸에서 OLLAMA_HOST=0.0.0.0:11434로 띄우면 되지만, 이 상태로 두면 재부팅 후 원래대로 돌아갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 systemd override로 영구 반영했습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo systemctl edit ollama
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[Service]
Environment=&quot;OLLAMA_HOST=0.0.0.0:11434&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반영:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl restart ollama
&lt;/code&gt;&lt;/pre&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 data-end=&quot;4794&quot; data-start=&quot;4773&quot; data-section-id=&quot;sepd09&quot; data-ke-size=&quot;size26&quot;&gt;연결은 됐는데 모델이 안 보일 때&lt;/h2&gt;
&lt;p data-end=&quot;4870&quot; data-start=&quot;4796&quot; data-ke-size=&quot;size16&quot;&gt;Open WebUI까지 연결하고 나면, 이번에는 모델 목록이 안 보이는 경우가 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;pre id=&quot;code_1773881304140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ollama list
curl http://127.0.0.1:11434/api/tags&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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;p data-end=&quot;5093&quot; data-start=&quot;4962&quot; data-ke-size=&quot;size16&quot;&gt;여기서 모델이 실제로 설치되어 있지 않으면, 당연히 Open WebUI에서도 목록이 비어 있습니다.&lt;br /&gt;그래서 ollama pull을 먼저 하고, 브라우저를 새로고침하거나 Open WebUI를 재기동하면 해결되는 경우가 많았습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1773881336124&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker compose restart&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5164&quot; data-start=&quot;5131&quot; data-ke-size=&quot;size16&quot;&gt;또는 로그아웃 후 다시 로그인해도 반영되는 경우가 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-end=&quot;5209&quot; data-start=&quot;5171&quot; data-section-id=&quot;bvk6m5&quot; data-ke-size=&quot;size26&quot;&gt;관리자 계정에서는 보이는데 다른 사용자에게는 모델이 안 보일 때&lt;/h2&gt;
&lt;p data-end=&quot;5304&quot; data-start=&quot;5211&quot; data-ke-size=&quot;size16&quot;&gt;이건 조금 더 헷갈리는 경우입니다.&lt;br /&gt;관리자 계정으로 접속하면 모델이 잘 보이는데, 일반 사용자 계정에서는 모델 목록이 비어 있거나 선택이 안 되는 경우가 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5388&quot; data-start=&quot;5306&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 Ollama 연결 문제로 생각하기 쉬운데,&lt;br /&gt;실제로는 &lt;b&gt;Open WebUI 내부 설정이나 사용자 노출 범위 문제&lt;/b&gt;인 경우가 많습니다.&lt;/p&gt;
&lt;p data-end=&quot;5416&quot; data-start=&quot;5390&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때는 아래 순서로 보는 게 가장 빠릅니다.&lt;/p&gt;
&lt;h3 data-end=&quot;5446&quot; data-start=&quot;5418&quot; data-section-id=&quot;dvg503&quot; data-ke-size=&quot;size23&quot;&gt;1) 모델이 실제로 설치돼 있는지 먼저 확인&lt;/h3&gt;
&lt;p data-end=&quot;5461&quot; data-start=&quot;5447&quot; data-ke-size=&quot;size16&quot;&gt;가장 기본적인 점검입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1773881319243&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ollama list&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5525&quot; data-start=&quot;5488&quot; data-ke-size=&quot;size16&quot;&gt;여기서 모델이 없으면 관리자든 일반 사용자든 안 보여야 정상입니다.&lt;/p&gt;
&lt;h3 data-end=&quot;5551&quot; data-start=&quot;5527&quot; data-section-id=&quot;p3vhqp&quot; data-ke-size=&quot;size23&quot;&gt;2) 관리자 계정에서는 보이는지 확인&lt;/h3&gt;
&lt;p data-end=&quot;5660&quot; data-start=&quot;5552&quot; data-ke-size=&quot;size16&quot;&gt;관리자 화면에서 모델이 보인다면, Ollama 연결과 모델 설치는 대체로 정상이라고 볼 수 있습니다.&lt;br /&gt;이 경우는 네트워크 문제보다는 &lt;b&gt;Open WebUI 설정 문제&lt;/b&gt;일 가능성이 높습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;5686&quot; data-start=&quot;5662&quot; data-section-id=&quot;rhxn9p&quot; data-ke-size=&quot;size23&quot;&gt;3) 일반 사용자 세션을 새로고침한다&lt;/h3&gt;
&lt;p data-end=&quot;5734&quot; data-start=&quot;5687&quot; data-ke-size=&quot;size16&quot;&gt;새 모델을 pull 한 뒤 일반 사용자 세션에는 바로 반영되지 않는 경우도 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;5760&quot; data-start=&quot;5736&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때는 아래 순서가 의외로 잘 먹힙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5831&quot; data-start=&quot;5762&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5773&quot; data-start=&quot;5762&quot; data-section-id=&quot;1wb11p0&quot;&gt;브라우저 새로고침&lt;/li&gt;
&lt;li data-end=&quot;5787&quot; data-start=&quot;5774&quot; data-section-id=&quot;19b6wbj&quot;&gt;로그아웃 후 재로그인&lt;/li&gt;
&lt;li data-end=&quot;5805&quot; data-start=&quot;5788&quot; data-section-id=&quot;1klggag&quot;&gt;새 채팅 화면으로 다시 진입&lt;/li&gt;
&lt;li data-end=&quot;5831&quot; data-start=&quot;5806&quot; data-section-id=&quot;jq2awl&quot;&gt;그래도 안 되면 Open WebUI 재기동&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;5899&quot; data-start=&quot;5869&quot; data-section-id=&quot;lnsxgm&quot; data-ke-size=&quot;size23&quot;&gt;4) 사용자 권한이나 모델 노출 설정을 확인한다&lt;/h3&gt;
&lt;p data-end=&quot;5989&quot; data-start=&quot;5900&quot; data-ke-size=&quot;size16&quot;&gt;관리자 계정에서는 보이는데 일반 사용자에게만 안 보인다면,&lt;br /&gt;대부분은 Open WebUI 쪽의 사용자 노출 설정이나 권한 범위를 먼저 의심하는 것이 맞습니다.&lt;/p&gt;
&lt;p data-end=&quot;6064&quot; data-start=&quot;5991&quot; data-ke-size=&quot;size16&quot;&gt;즉 이런 문제는 보통 GPU나 Ollama 문제라기보다&lt;br /&gt;**&amp;ldquo;모델은 있는데 사용자 화면에 노출되지 않는 문제&amp;rdquo;**에 가깝습니다.&lt;/p&gt;
&lt;p data-end=&quot;6096&quot; data-start=&quot;6066&quot; data-ke-size=&quot;size16&quot;&gt;정리하면, 이런 경우에는 아래 순서로 점검하면 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;6227&quot; data-start=&quot;6098&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;6127&quot; data-start=&quot;6098&quot; data-section-id=&quot;1ei7ppj&quot;&gt;ollama list 로 모델 설치 확인&lt;/li&gt;
&lt;li data-end=&quot;6152&quot; data-start=&quot;6128&quot; data-section-id=&quot;137jzht&quot;&gt;관리자 계정에서 모델 보이는지 확인&lt;/li&gt;
&lt;li data-end=&quot;6174&quot; data-start=&quot;6153&quot; data-section-id=&quot;1tvztsa&quot;&gt;일반 사용자 새로고침/재로그인&lt;/li&gt;
&lt;li data-end=&quot;6204&quot; data-start=&quot;6175&quot; data-section-id=&quot;4w7d40&quot;&gt;Open WebUI 권한 및 노출 설정 확인&lt;/li&gt;
&lt;li data-end=&quot;6227&quot; data-start=&quot;6205&quot; data-section-id=&quot;1djrvsf&quot;&gt;필요 시 Open WebUI 재기동&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;써보니 느낀 점 (핵심 정리)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ollama 설치 자체는 어렵지 않았습니다. 진짜 중요한 건 &lt;b&gt;설치 후 어떻게 연결해서 실제로 쓰느냐&lt;/b&gt;였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Linux에서는 아래 3가지를 꼭 확인해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ollama가 어느 인터페이스에 바인딩되어 있는지&lt;/li&gt;
&lt;li&gt;Docker 컨테이너가 호스트를 어떻게 바라보는지&lt;/li&gt;
&lt;li&gt;Open WebUI가 실제로 어느 주소를 호출하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분만 맞추면, 이후에는 생각보다 편하게 로컬 LLM 환경을 쓸 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 비교 테스트&lt;/li&gt;
&lt;li&gt;프롬프트 실험&lt;/li&gt;
&lt;li&gt;백엔드 연동&lt;/li&gt;
&lt;li&gt;로컬 AI 채팅 환경 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업의 목적은 &amp;ldquo;최고 성능의 AI 서비스&amp;rdquo;가 아니라, &lt;b&gt;클라우드 없이도 돌아가는 로컬 LLM 테스트 환경을 직접 구축&lt;/b&gt;해보는 것이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ubuntu에 Ollama를 설치하고, GTX 1080 장비에서 가볍게 모델을 실행한 뒤, Open WebUI까지 붙여 브라우저에서 테스트해보니 꽤 실용적이라는 느낌을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 프로젝트나 초기 MVP 단계에서 &amp;ldquo;일단 클라우드 비용 없이 한 번 돌려보고 싶다&amp;rdquo;는 생각이 있다면, &lt;b&gt;Ollama + Open WebUI 조합&lt;/b&gt;은 좋은 출발점이 될 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;6992&quot; data-start=&quot;6887&quot; 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;1908&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFYqtT/dJMcagdK2Gl/kr43UAB9BmAykNlOamyKq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFYqtT/dJMcagdK2Gl/kr43UAB9BmAykNlOamyKq0/img.png&quot; data-alt=&quot;open web ui 채팅창&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFYqtT/dJMcagdK2Gl/kr43UAB9BmAykNlOamyKq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFYqtT%2FdJMcagdK2Gl%2Fkr43UAB9BmAykNlOamyKq0%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;1908&quot; height=&quot;700&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;open web ui 채팅창&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;6992&quot; data-start=&quot;6887&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI개발</category>
      <category>AI챗봇</category>
      <category>chatgpt대체</category>
      <category>LLM</category>
      <category>llm구축</category>
      <category>ollama</category>
      <category>openwebui</category>
      <category>ubuntu</category>
      <category>로컬ai</category>
      <category>로컬llm</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/56</guid>
      <comments>https://threemealsofcoding.tistory.com/56#entry56comment</comments>
      <pubDate>Thu, 19 Mar 2026 09:52:33 +0900</pubDate>
    </item>
    <item>
      <title>Jenkins 기반 CI/CD 1차 검증 정리 (정적 웹 + Docker 배포)</title>
      <link>https://threemealsofcoding.tistory.com/55</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포서버를 &lt;b&gt;Jenkins 기반 CI/CD 전용 서버&lt;/b&gt;로 활용하기 위한 1차 검증 내용을 정리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 &lt;b&gt;정적 웹 샘플 프로젝트&lt;/b&gt;를 기준으로, 아래의 End-to-End 흐름이 실제로 한 번 끝까지 정상 동작하는지 확인하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브랜치 작업 &amp;rarr; Git push&lt;/li&gt;
&lt;li&gt;Jenkins Pipeline 실행&lt;/li&gt;
&lt;li&gt;Docker 이미지 빌드&lt;/li&gt;
&lt;li&gt;이미지 tar 산출물 생성&lt;/li&gt;
&lt;li&gt;개발서버 전송&lt;/li&gt;
&lt;li&gt;개발서버에서 이미지 로드 및 컨테이너 기동&lt;/li&gt;
&lt;li&gt;개발서버 내부에서 curl로 응답 확인&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;h3 data-ke-size=&quot;size23&quot;&gt;포함&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins 설치 및 기동 확인&lt;/li&gt;
&lt;li&gt;Jenkins 실행 Java 21 적용&lt;/li&gt;
&lt;li&gt;Git 설치 및 저장소 연동&lt;/li&gt;
&lt;li&gt;개발서버 배포 계정 생성 및 SSH 키 기반 접속 구성&lt;/li&gt;
&lt;li&gt;Pipeline 기반 checkout&lt;/li&gt;
&lt;li&gt;Docker 이미지 빌드&lt;/li&gt;
&lt;li&gt;이미지 tar 생성&lt;/li&gt;
&lt;li&gt;개발서버 전송(SCP)&lt;/li&gt;
&lt;li&gt;개발서버에서 docker load 및 테스트 컨테이너 기동&lt;/li&gt;
&lt;li&gt;개발서버 내부 curl 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제외&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영서버 배포&lt;/li&gt;
&lt;li&gt;Git webhook 또는 polling 자동 트리거&lt;/li&gt;
&lt;li&gt;Spring Boot 및 Maven 빌드&lt;/li&gt;
&lt;li&gt;무중단 배포&lt;/li&gt;
&lt;li&gt;스테이징 환경&lt;/li&gt;
&lt;li&gt;사설 Docker Registry&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;h3 data-ke-size=&quot;size23&quot;&gt;배포서버( Jenkins 서버 )&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할: &lt;b&gt;CI/CD 실행 및 빌드 서버&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;Jenkins, Java 21, Git, Docker 설치&lt;/li&gt;
&lt;li&gt;Pipeline 실행&lt;/li&gt;
&lt;li&gt;Docker 이미지 빌드 및 산출물 생성&lt;/li&gt;
&lt;li&gt;개발서버로 산출물 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발서버&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할: &lt;b&gt;1차 배포 및 검증 대상 서버&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포용 계정 devops 생성&lt;/li&gt;
&lt;li&gt;공개키 등록&lt;/li&gt;
&lt;li&gt;Docker 설치 및 기동&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;사전 준비(체크리스트)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins 설치 및 기동 완료&lt;/li&gt;
&lt;li&gt;Jenkins 접속 포트 9090 적용&lt;/li&gt;
&lt;li&gt;Java 21 적용 완료&lt;/li&gt;
&lt;li&gt;Git 설치 완료&lt;/li&gt;
&lt;li&gt;Docker 설치 완료&lt;/li&gt;
&lt;li&gt;Jenkins Credentials에 개발서버 SSH 키 등록 완료
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kind: SSH Username with private key&lt;/li&gt;
&lt;li&gt;Username: devops&lt;/li&gt;
&lt;li&gt;Credential ID: ssh-devops-devserver&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배포서버에서 devops@개발서버 SSH 접속 확인 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Git 저장소 및 샘플 구조&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브랜치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feature/static-cicd-test&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디렉터리 구조(예시)&lt;/h3&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;project-root/
 ├─ Jenkinsfile
 ├─ (기존 Java 프로젝트 파일)
 └─ cicd-static-sample/
     ├─ index.html
     └─ Dockerfile
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;샘플 파일&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;index.html&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
	&amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
	&amp;lt;title&amp;gt;Hello Jenkins&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
	&amp;lt;h1&amp;gt;Hello Jenkins CI/CD&amp;lt;/h1&amp;gt;
	&amp;lt;p&amp;gt;Static Web Deploy Success&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Dockerfile&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
&lt;/code&gt;&lt;/pre&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;이번 1차 검증에서 핵심은 &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;원격 Git 저장소: 소스 원본&lt;/li&gt;
&lt;li&gt;배포서버 Jenkins Workspace: checkout 된 소스가 존재하는 작업 공간&lt;/li&gt;
&lt;li&gt;배포서버 Docker 엔진: 이미지 빌드 수행 위치&lt;/li&gt;
&lt;li&gt;개발서버: 컨테이너 실행 및 검증 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Pipeline은 저장소에서 파일을 직접 조작하는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소스를 Jenkins Workspace로 가져온 뒤(Checkout) 배포서버에서 빌드하고, 산출물을 개발서버로 전달&lt;/b&gt;하는 구조입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Jenkins Workspace 확인 팁&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins 서버 쉘&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;ls -al /var/lib/jenkins/workspace
ls -al /var/lib/jenkins/workspace/작업명
&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;Pipeline에서 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sh 'pwd'
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dir('cicd-static-sample') 의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 구문은 셸의 cd cicd-static-sample과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;dir('cicd-static-sample') {
	sh 'docker build -t sample-static-web:static-cicd-test .'
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 웹 샘플과 Dockerfile이 프로젝트 루트가 아니라 &lt;b&gt;하위 폴더&lt;/b&gt;에 있으므로, docker build의 기준 경로를 맞추기 위해 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;산출물(Artifact) 관리 정책&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;저장 경로&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;/data/jenkins-artifacts/static-web
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파일 예시&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;/data/jenkins-artifacts/static-web/sample-static-web-static-cicd-test.tar
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 테스트 산출물 1개 기준으로 덮어쓰기&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;Jenkins Pipeline 스크립트(예시)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stage 구성:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Checkout&lt;/li&gt;
&lt;li&gt;Check Files&lt;/li&gt;
&lt;li&gt;Build Image&lt;/li&gt;
&lt;li&gt;Save Image&lt;/li&gt;
&lt;li&gt;Transfer Image&lt;/li&gt;
&lt;li&gt;Deploy To Dev&lt;/li&gt;
&lt;li&gt;Verify On Dev&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 스크립트의 원격저장소URL, 개발서버IP는 실제 값으로 교체하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;pipeline {
	agent any

	stages {
		stage('Checkout') {
			steps {
				git branch: 'feature/static-cicd-test',
					credentialsId: 'git-sample-creds',
					url: '원격저장소URL'
			}
		}

		stage('Check Files') {
			steps {
				sh 'pwd'
				sh 'ls -al'
				sh 'ls -al cicd-static-sample'
				sh 'cat cicd-static-sample/index.html'
			}
		}

		stage('Build Image') {
			steps {
				dir('cicd-static-sample') {
					sh 'docker build -t sample-static-web:static-cicd-test .'
				}
			}
		}

		stage('Save Image') {
			steps {
				sh '''
					rm -f /data/jenkins-artifacts/static-web/sample-static-web-static-cicd-test.tar
					docker save -o /data/jenkins-artifacts/static-web/sample-static-web-static-cicd-test.tar sample-static-web:static-cicd-test
					ls -lh /data/jenkins-artifacts/static-web/
				'''
			}
		}

		stage('Transfer Image') {
			steps {
				withCredentials([sshUserPrivateKey(
					credentialsId: 'ssh-devops-devserver',
					keyFileVariable: 'SSH_KEY',
					usernameVariable: 'SSH_USER'
				)]) {
					sh '''
						ssh -o StrictHostKeyChecking=no -i &quot;$SSH_KEY&quot; ${SSH_USER}@개발서버IP 'mkdir -p /home/devops/deploy'
						scp -o StrictHostKeyChecking=no -i &quot;$SSH_KEY&quot; \\
							/data/jenkins-artifacts/static-web/sample-static-web-static-cicd-test.tar \\
							${SSH_USER}@개발서버IP:/home/devops/deploy/
					'''
				}
			}
		}

		stage('Deploy To Dev') {
			steps {
				withCredentials([sshUserPrivateKey(
					credentialsId: 'ssh-devops-devserver',
					keyFileVariable: 'SSH_KEY',
					usernameVariable: 'SSH_USER'
				)]) {
					sh '''
						ssh -o StrictHostKeyChecking=no -i &quot;$SSH_KEY&quot; ${SSH_USER}@개발서버IP '
							docker load -i /home/devops/deploy/sample-static-web-static-cicd-test.tar &amp;amp;&amp;amp;
							docker rm -f sample-static-web-test || true &amp;amp;&amp;amp;
							docker run -d --name sample-static-web-test -p 8082:80 sample-static-web:static-cicd-test &amp;amp;&amp;amp;
							docker ps | grep sample-static-web-test
						'
					'''
				}
			}
		}

		stage('Verify On Dev') {
			steps {
				withCredentials([sshUserPrivateKey(
					credentialsId: 'ssh-devops-devserver',
					keyFileVariable: 'SSH_KEY',
					usernameVariable: 'SSH_USER'
				)]) {
					sh '''
						ssh -o StrictHostKeyChecking=no -i &quot;$SSH_KEY&quot; ${SSH_USER}@개발서버IP '
							curl -o /dev/null -s -w &quot;%{http_code}\\n&quot; &amp;lt;http://127.0.0.1:8082&amp;gt;
						'
					'''
				}
			}
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발서버에서 수동 검증 명령(필요 시)&lt;/h2&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 컨테이너 확인
docker ps

# 응답 확인
curl -s &amp;lt;http://127.0.0.1:8082&amp;gt;

# HTTP 상태코드만 확인
curl -o /dev/null -s -w &quot;%{http_code}\\n&quot; &amp;lt;http://127.0.0.1:8082&amp;gt;

# 이미지 확인
docker images
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 컨테이너 운영 정책&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 개발 컨테이너와 충돌을 피하기 위해 테스트용으로 분리했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지: sample-static-web:static-cicd-test&lt;/li&gt;
&lt;li&gt;컨테이너: sample-static-web-test&lt;/li&gt;
&lt;li&gt;포트: 8082:80&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;h3 data-ke-size=&quot;size23&quot;&gt;확인된 사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins 기동 정상&lt;/li&gt;
&lt;li&gt;Java 21 적용 정상&lt;/li&gt;
&lt;li&gt;Git checkout 정상&lt;/li&gt;
&lt;li&gt;Docker 이미지 빌드 정상&lt;/li&gt;
&lt;li&gt;tar 산출물 생성 정상&lt;/li&gt;
&lt;li&gt;배포서버 &amp;rarr; 개발서버 전송 정상&lt;/li&gt;
&lt;li&gt;개발서버에서 이미지 로드 및 컨테이너 기동 정상&lt;/li&gt;
&lt;li&gt;개발서버 내부 curl 검증에서 HTTP 200 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 웹 샘플 기준으로 &lt;b&gt;Jenkins 기반 CI/CD 1차 검증은 정상 완료&lt;/b&gt;되었습니다.&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;다음 단계에서는 &lt;b&gt;Spring Boot + Maven + Docker&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;Spring Boot 샘플 구성&lt;/li&gt;
&lt;li&gt;Maven compile 및 package&lt;/li&gt;
&lt;li&gt;Docker 이미지 생성 및 배포&lt;/li&gt;
&lt;li&gt;Git webhook 또는 polling 기반 자동 트리거 검토&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>시스템 관리/CI_CD</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/55</guid>
      <comments>https://threemealsofcoding.tistory.com/55#entry55comment</comments>
      <pubDate>Fri, 13 Mar 2026 14:47:03 +0900</pubDate>
    </item>
    <item>
      <title>젠킨스 환경 구축 가이드</title>
      <link>https://threemealsofcoding.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 컨테이너를 사용하지 않고 서버에 젠킨스 설치 과정으로 진행 했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 설치 과정을 참고 하고 싶은 분들에게 권장합니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준 환경&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;작성일: 2026-03-12&lt;/li&gt;
&lt;li&gt;OS: Rocky Linux 8.0&lt;/li&gt;
&lt;li&gt;패키지 매니저: dnf&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 관리자 실계정 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ci/cd 관리 계정을 생성한다. root가 아닌 sudo 권한으로 진행 하기 위해 생성 했다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo useradd -m -s /bin/bash devops
sudo passwd devops
sudo usermod -aG wheel devops
id devops
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Java 설치 (OpenJDK 21)&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;sudo dnf install -y java-21-openjdk java-21-openjdk-devel
java -version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Jenkins 공식 저장소 등록&lt;/h3&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;sudo rpm --import &amp;lt;https://pkg.jenkins.io/redhat-stable/jenkins.io-2026.key&amp;gt;

sudo tee /etc/yum.repos.d/jenkins.repo &amp;gt; /dev/null &amp;lt;&amp;lt;'EOF'
[jenkins]
name=Jenkins-stable
baseurl=https://pkg.jenkins.io/redhat-stable
gpgcheck=1
repo_gpgcheck=1
enabled=1
gpgkey=https://pkg.jenkins.io/redhat-stable/jenkins.io-2026.key
EOF

sudo dnf repolist | grep jenkins
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) Jenkins 설치&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;sudo dnf install -y jenkins
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) Jenkins 기동&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins --no-pager
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6) 계정 및 경로 확인&lt;/h3&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;id jenkins
getent passwd jenkins
ls -ld /var/lib/jenkins
ls -ld /var/log/jenkins
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7) 초기 비밀번호 확인&lt;/h3&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;sudo cat /var/lib/jenkins/secrets/initialAdminPassword
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8) 방화벽 오픈 (8080)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방화벽 기동시에만 추가한다. firewalld 를 쓰지 않는다면 해당 없음&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 부터는 필수 진행 조건은 아니니 필요하면 추가하도록 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9) Jenkins 포트 변경 (예: 9090)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젠킨스 포트를 기본 포트 말고 다른 포트로 변경시에만 진행&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo systemctl edit jenkins
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용을 추가:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[Service]
Environment=&quot;JENKINS_PORT=9090&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl restart jenkins
sudo systemctl status jenkins --no-pager
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10) 호스트 등록&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;192.168.1.92 jenkins.dev.local&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://jenkins.dev.local:9090 으로 접속 확인&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;호스트 등록하지 않고 기본 접속 으로 사용시 http://localhost:8080 접속&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;11) 초기비밀번호 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1773279792585&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo cat /var/lib/jenkins/secrets/initialAdminPassword&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;초기비밀번호를 copy 하여 그대로 web 화면에 붙여넣고 진행 한다.&lt;/p&gt;</description>
      <category>시스템 관리/CI_CD</category>
      <category>젠킨스</category>
      <category>젠킨스설치</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/54</guid>
      <comments>https://threemealsofcoding.tistory.com/54#entry54comment</comments>
      <pubDate>Thu, 12 Mar 2026 10:45:37 +0900</pubDate>
    </item>
    <item>
      <title>하이퍼렛저 패브릭</title>
      <link>https://threemealsofcoding.tistory.com/53</link>
      <description>&lt;h1&gt;1. 하이퍼렛저 패브릭이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼렛저 패브릭은 기업/기관 간 거래를 신뢰성 있게 기록하고 검증하기 위한 허가형 블록체인 플랫폼이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무나 참여하는 공개 블록체인이 아니라 허가받은 회사/기관만 참여 거래내역을 여러 참여자가 함께 검증하고 공유 위변조가 어렵고, 거래 이력을 투명하게 남길 수 있는 구조&lt;/p&gt;
&lt;h1&gt;2. 핵심 구성 요소&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 원장(Ledger)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원장은 블록체인에서 거래내역이 저장되는 장부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패브릭의 원장은 크게 두 부분으로 생각하면 쉽다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Blockchain
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거래가 블록 단위로 차곡차곡 쌓이는 영역&lt;/li&gt;
&lt;li&gt;과거 이력이 계속 남음&lt;/li&gt;
&lt;li&gt;변경 이력 추적 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;World State
&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;예: 현재 주문 상태가 APPROVED 인지 SETTLED 인지 바로 조회 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 블록(Block)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록은 여러 거래를 묶어서 저장하는 단위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거래 1: 주문 생성&lt;/li&gt;
&lt;li&gt;거래 2: 주문 승인&lt;/li&gt;
&lt;li&gt;거래 3: 정산 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 거래 여러 개가 하나의 블록에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록은 이전 블록과 연결되므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간의 내용을 바꾸면 전체 연결이 깨지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위변조가 어렵다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.3 트랜잭션(Transaction)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션은 원장에 기록되는 하나의 업무 행위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주문 등록&lt;/li&gt;
&lt;li&gt;승인 처리&lt;/li&gt;
&lt;li&gt;상태 변경&lt;/li&gt;
&lt;li&gt;정산 처리&lt;/li&gt;
&lt;li&gt;거래 취소&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 거래시스템에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 업무 요청 하나하나가 패브릭의 트랜잭션이 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.4 피어(Peer)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피어는 블록체인 네트워크에 참여하는 서버다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원장 보관&lt;/li&gt;
&lt;li&gt;체인코드 실행&lt;/li&gt;
&lt;li&gt;거래 검증&lt;/li&gt;
&lt;li&gt;블록 수신 및 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 피어는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;실제 데이터를 들고 있으면서 거래를 검사하는 서버&amp;rdquo;&lt;/b&gt; 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 A, 회사 B가 각각 피어를 운영할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 서로 같은 거래를 공유하게 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.5 오더러(Orderer)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오더러는 거래의 순서를 정리해서 블록으로 묶는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인에서는 거래가 동시에 많이 들어오므로 순서를 정해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오더러가 하는 일은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거래 요청 수집&lt;/li&gt;
&lt;li&gt;순서 정렬&lt;/li&gt;
&lt;li&gt;블록 생성&lt;/li&gt;
&lt;li&gt;각 피어에게 블록 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 오더러는 &lt;b&gt;&amp;ldquo;거래를 정렬해서 블록으로 만들어 배포하는 서버&amp;rdquo;&lt;/b&gt; 다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.6 채널(Channel)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널은 참여자별로 원장을 분리하는 논리적 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A기관과 B기관만 보는 거래 채널&lt;/li&gt;
&lt;li&gt;A기관, B기관, 감독기관이 함께 보는 감사 채널&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 같은 패브릭 네트워크 안에서도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 데이터를 모두가 보는 것이 아니라, 필요한 참여자끼리만 공유&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거래시스템에서는 매우 중요한 개념이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.7 체인코드(Chaincode)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체인코드는 패브릭에서 실행되는 업무 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 &lt;b&gt;블록체인용 프로그램&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 체인코드 안에 이런 로직을 넣을 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주문 생성&lt;/li&gt;
&lt;li&gt;이미 존재하는 주문번호인지 검증&lt;/li&gt;
&lt;li&gt;승인 권한 체크&lt;/li&gt;
&lt;li&gt;상태 전이 검증&lt;/li&gt;
&lt;li&gt;정산 완료 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 일반 시스템의 서비스 로직 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공동 검증이 필요한 핵심 규칙&lt;/b&gt;을 체인코드로 넣는다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패브릭의 체인코드는 흔히 &amp;ldquo;스마트 컨트랙트&amp;rdquo;라고도 부른다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.8 조직(Organization)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패브릭은 여러 기관이 참여하는 구조라서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 참여 주체를 &lt;b&gt;조직(Org)&lt;/b&gt; 으로 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Org1 = 거래기관 A&lt;/li&gt;
&lt;li&gt;Org2 = 거래기관 B&lt;/li&gt;
&lt;li&gt;Org3 = 정산기관&lt;/li&gt;
&lt;li&gt;Org4 = 감사기관&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 조직은 자기 피어, 사용자, 인증서를 가진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.9 MSP(Membership Service Provider)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSP는 네트워크 참가자의 신원을 식별하는 체계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 사용자가 누구인지&lt;/li&gt;
&lt;li&gt;이 서버가 어느 조직 소속인지&lt;/li&gt;
&lt;li&gt;이 요청이 믿을 수 있는 인증서인지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 판단하는 기준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 MSP는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;패브릭에서 신분 확인을 담당하는 체계&amp;rdquo;&lt;/b&gt; 다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.10 CA(Certificate Authority)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CA는 인증서를 발급하는 기관이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패브릭에서는 사용자나 피어에게 인증서를 발급해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누가 누구인지 증명하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관리자 인증서&lt;/li&gt;
&lt;li&gt;애플리케이션 사용자 인증서&lt;/li&gt;
&lt;li&gt;피어 서버 인증서&lt;/li&gt;
&lt;li&gt;오더러 인증서&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 CA는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;참여자에게 전자신분증을 발급하는 기관&amp;rdquo;&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참조: Network vs Channel (Hyperledger Fabric)&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;b&gt;네트워크(Network)&lt;/b&gt;: Fabric 기반 인프라 전체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성: Orderer, Peer, CA, 조직, 인증 체계&lt;/li&gt;
&lt;li&gt;비유: 도로, 건물, 전기 같은 기반 시설&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;채널(Channel)&lt;/b&gt;: 네트워크 위에 만들어지는 업무별 공동 원장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의미: 실제 거래가 기록되는 논리적 공간&lt;/li&gt;
&lt;li&gt;비유: 오피스 빌딩 안의 회의실&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비유 정리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 = 오피스 빌딩&lt;/li&gt;
&lt;li&gt;채널 = 빌딩 안의 회의실&lt;/li&gt;
&lt;li&gt;피어 = 회의에 참가하는 사람 또는 장비&lt;/li&gt;
&lt;li&gt;원장 = 회의록 &amp;lt;/aside&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>블록체인</category>
      <category>하이퍼렛저패브릭</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/53</guid>
      <comments>https://threemealsofcoding.tistory.com/53#entry53comment</comments>
      <pubDate>Mon, 9 Mar 2026 17:49:47 +0900</pubDate>
    </item>
    <item>
      <title>Java GC(Garbage Collection) 기초 개념 정리</title>
      <link>https://threemealsofcoding.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서의 메모리 관리는 자동으로 이루어지며, 그 중심에는 Garbage Collector(GC)가 있습니다. 이번 글에서는 GC의 기본 원리와 각 영역의 역할, 그리고 객체가 살아가는 흐름에 대해 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;271&quot; data-start=&quot;256&quot; data-ke-size=&quot;size26&quot;&gt;1. GC란 무엇인가?&lt;/h2&gt;
&lt;p data-end=&quot;416&quot; data-start=&quot;273&quot; data-ke-size=&quot;size16&quot;&gt;GC(Garbage Collection)는 프로그램 실행 중 더 이상 사용되지 않는 객체를 자동으로 메모리에서 제거하여, 메모리를 효율적으로 관리하는 JVM의 기능입니다. Java에서는 개발자가 명시적으로 메모리를 해제하지 않아도 GC가 이를 대신해줍니다.&lt;/p&gt;
&lt;p data-end=&quot;416&quot; data-start=&quot;273&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;449&quot; data-start=&quot;423&quot; data-ke-size=&quot;size26&quot;&gt;2. JVM 메모리 구조와 GC 관련 영역&lt;/h2&gt;
&lt;p data-end=&quot;477&quot; data-start=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;JVM의 힙(Heap)은 다음과 같이 나뉩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;715&quot; data-start=&quot;479&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;588&quot; data-start=&quot;479&quot;&gt;&lt;b&gt;Young Generation&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;588&quot; data-start=&quot;504&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;534&quot; data-start=&quot;504&quot;&gt;&lt;b&gt;Eden 영역&lt;/b&gt;: 새로 생성된 객체들이 저장됨&lt;/li&gt;
&lt;li data-end=&quot;588&quot; data-start=&quot;537&quot;&gt;&lt;b&gt;Survivor 영역(S0, S1)&lt;/b&gt;: Eden에서 살아남은 객체들이 이동하는 공간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;654&quot; data-start=&quot;589&quot;&gt;&lt;b&gt;Old Generation&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;654&quot; data-start=&quot;612&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;654&quot; data-start=&quot;612&quot;&gt;여러 번의 GC를 견디고 살아남은 객체가 승격(promote)되어 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;715&quot; data-start=&quot;655&quot;&gt;&lt;b&gt;Metaspace&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;715&quot; data-start=&quot;673&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;715&quot; data-start=&quot;673&quot;&gt;클래스 메타데이터가 저장되는 공간 (JDK 8부터 PermGen을 대체)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;737&quot; data-start=&quot;722&quot; data-ke-size=&quot;size26&quot;&gt;3. GC의 동작 원리&lt;/h2&gt;
&lt;h3 data-end=&quot;762&quot; data-start=&quot;739&quot; data-ke-size=&quot;size23&quot;&gt;(1) 객체 생성과 Minor GC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;948&quot; data-start=&quot;764&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;796&quot; data-start=&quot;764&quot;&gt;새로 생성된 객체는 &lt;b&gt;Eden 영역&lt;/b&gt;에 저장됩니다.&lt;/li&gt;
&lt;li data-end=&quot;904&quot; data-start=&quot;797&quot;&gt;Eden이 가득 차면 &lt;b&gt;Minor GC&lt;/b&gt;가 발생하여,
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;904&quot; data-start=&quot;833&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;866&quot; data-start=&quot;833&quot;&gt;GC Root에서 &lt;b&gt;참조되지 않는 객체&lt;/b&gt;는 제거되고,&lt;/li&gt;
&lt;li data-end=&quot;904&quot; data-start=&quot;869&quot;&gt;살아남은 객체는 &lt;b&gt;Survivor 영역&lt;/b&gt;으로 이동합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;948&quot; data-start=&quot;905&quot;&gt;이 과정을 거치며 객체는 &lt;b&gt;age(생존 횟수)&lt;/b&gt; 를 증가시키게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;981&quot; data-start=&quot;950&quot; data-ke-size=&quot;size23&quot;&gt;(2) Survivor &amp;rarr; Old 영역으로의 승격&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1113&quot; data-start=&quot;983&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1073&quot; data-start=&quot;983&quot;&gt;Survivor 영역에 있던 객체가 &lt;b&gt;여러 번의 Minor GC에서도 살아남으면&lt;/b&gt;,&lt;br /&gt;&amp;rarr; 일정 age 이상일 경우 &lt;b&gt;Old 영역&lt;/b&gt;으로 승격됩니다.&lt;/li&gt;
&lt;li data-end=&quot;1113&quot; data-start=&quot;1074&quot;&gt;Old 영역은 &lt;b&gt;장기적으로 참조되는 객체&lt;/b&gt;들이 모이는 곳입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1130&quot; data-start=&quot;1115&quot; data-ke-size=&quot;size23&quot;&gt;(3) Full GC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1203&quot; data-start=&quot;1132&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1149&quot; data-start=&quot;1132&quot;&gt;Old 영역이 가득 차거나,&lt;/li&gt;
&lt;li data-end=&quot;1182&quot; data-start=&quot;1150&quot;&gt;System.gc()와 같은 명시적 호출이 있거나,&lt;/li&gt;
&lt;li data-end=&quot;1203&quot; data-start=&quot;1183&quot;&gt;Metaspace가 부족해질 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1267&quot; data-start=&quot;1205&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;Full GC&lt;/b&gt;가 발생하여 Young, Old, Metaspace 영역까지 모두 GC의 대상이 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1267&quot; data-start=&quot;1205&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1297&quot; data-start=&quot;1274&quot; data-ke-size=&quot;size26&quot;&gt;4. GC Root와 객체 생존 여부&lt;/h2&gt;
&lt;p data-end=&quot;1405&quot; data-start=&quot;1299&quot; data-ke-size=&quot;size16&quot;&gt;GC는 단순히 &quot;얼마나 오래 있었는가&quot;를 기준으로 객체를 제거하지 않습니다.&lt;br /&gt;대신 &lt;b&gt;GC Root에서 참조 가능한지(Reachable)&lt;/b&gt; 여부를 기준으로 객체 생존 여부를 판단합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;1427&quot; data-start=&quot;1407&quot; data-ke-size=&quot;size23&quot;&gt;대표적인 GC Root 예시:&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1497&quot; data-start=&quot;1428&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1460&quot; data-start=&quot;1428&quot;&gt;현재 실행 중인 쓰레드의 스택 프레임 (지역 변수 등)&lt;/li&gt;
&lt;li data-end=&quot;1472&quot; data-start=&quot;1461&quot;&gt;static 변수&lt;/li&gt;
&lt;li data-end=&quot;1497&quot; data-start=&quot;1473&quot;&gt;JNI(Global references)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1526&quot; data-start=&quot;1504&quot; data-ke-size=&quot;size26&quot;&gt;5. 정리된 객체의 생존 흐름 요약&lt;/h2&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1754531536599&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Eden &amp;rarr; Survivor &amp;rarr; Old&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1701&quot; data-start=&quot;1559&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1611&quot; data-start=&quot;1559&quot;&gt;Eden에 생성된 객체 중 &lt;b&gt;GC Root에서 참조되지 않는 객체&lt;/b&gt;는 GC에 의해 제거&lt;/li&gt;
&lt;li data-end=&quot;1644&quot; data-start=&quot;1612&quot;&gt;살아남은 객체는 Survivor로 이동하고 age 증가&lt;/li&gt;
&lt;li data-end=&quot;1671&quot; data-start=&quot;1645&quot;&gt;age가 기준 이상이면 Old 영역으로 승격&lt;/li&gt;
&lt;li data-end=&quot;1701&quot; data-start=&quot;1672&quot;&gt;Old 영역이 가득 차면 Full GC 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1714&quot; data-start=&quot;1708&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;1871&quot; data-start=&quot;1716&quot; data-ke-size=&quot;size16&quot;&gt;Java의 GC는 매우 똑똑하게 설계되어 있지만, 정확히 이해하고 있어야 성능 병목을 줄이고, 적절한 튜닝도 가능해집니다. 이번 글에서는 기본적인 흐름만 정리했지만, 이후에는 다양한 GC 알고리즘(Serial, Parallel, G1 등)과 튜닝 방법까지 확장해볼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1871&quot; data-start=&quot;1716&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1871&quot; data-start=&quot;1716&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1953&quot; data-start=&quot;1878&quot; data-ke-size=&quot;size16&quot;&gt;이 포스트는 Java GC 개념을 정리하고 싶을 때 참고하기 위한 목적으로 작성되었습니다.&lt;br /&gt;필요하신 분들께 도움이 되기를 바랍니다.&lt;/p&gt;</description>
      <category>프로그래밍 언어/JAVA</category>
      <category>Eden 영역</category>
      <category>full gc</category>
      <category>garbage collection</category>
      <category>java gc</category>
      <category>JVM 메모리 구조</category>
      <category>old generation</category>
      <category>Survivor 영역</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/52</guid>
      <comments>https://threemealsofcoding.tistory.com/52#entry52comment</comments>
      <pubDate>Thu, 7 Aug 2025 10:53:55 +0900</pubDate>
    </item>
    <item>
      <title>Docker &amp;quot;Failed to start docker.service: Unit docker.service not found&amp;quot; 오류</title>
      <link>https://threemealsofcoding.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Docker가 snap 기반으로 설치 되어 서비스가 정상 기동이 되지 않을 경우 해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. docker가 Snap인지 확인 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754355396972&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#which docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;/snap/bin/docker로 나오면 Snap 기반&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;2. Snap으로 설치된 Docker 제거&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754355560015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo snap remove docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Snap 대신 Docker 공식 설치로 전환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker 공식 리포지터리를 통한 설치 (Ubuntu 24.04 기준)&lt;/p&gt;
&lt;pre id=&quot;code_1754355496875&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  &quot;deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable&quot; | \
  sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null

sudo apt update

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 서비스 기동&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754355527482&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl enable --now docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 도커 테스트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754355661754&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;gt; Hello from Docker !&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Snap으로 Docker를 사용할 때의 주요 문제점&lt;/b&gt; &lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;문제 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;보안 격리&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Snap 앱은 &lt;b&gt;sandbox 환경&lt;/b&gt;에서 실행되므로, 시스템 리소스 접근(예: /var/run/docker.sock, 특정 마운트 경로 등)에 &lt;b&gt;제한&lt;/b&gt;이 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;경로 문제&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;docker.sock 파일이 표준 경로(/var/run/docker.sock)가 아닌 Snap 전용 경로에 위치하기 때문에, 기존 스크립트나 도구들이 &lt;b&gt;정상 작동하지 않을 수 있습니다&lt;/b&gt;. 예: docker-compose, Portainer, Jenkins 등과의 연동 실패.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;도커 Compose 연동 문제&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;docker-compose가 Docker 데몬에 접근하지 못해 &lt;b&gt;Cannot connect to the Docker daemon&lt;/b&gt; 등의 오류가 발생할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;서비스 제어 불가&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;일반적으로 사용하는 systemctl start docker 등의 명령어가 작동하지 않으며, 대신 snap start docker 등의 Snap 전용 명령어를 사용해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;서드파티 도구와 호환성 저하&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;일부 서드파티 툴은 Snap Docker를 인식하지 못하거나, 특수 경로 문제로 인해 실행에 실패합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;리눅스 커널 기능 제한&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Snap 환경의 제한으로 인해 Cgroups, OverlayFS 등 특정 커널 기능 사용이 제한되어 Docker의 일부 기능이 정상적으로 작동하지 않을 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;버전 제어 어려움&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Docker 공식 릴리스와 버전 차이가 있으며, 특정 버전으로 고정하기도 어렵습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;볼륨 및 마운트 제한&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Snap 외부 경로에 대한 볼륨 마운트 시 권한 이슈가 발생하며, 별도의 권한 부여가 필요하거나 마운트에 실패할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snap Docker는 일반 사용자/테스트 용도로는 괜찮을 수 있지만, 실제 개발/운영 환경에서는 문제 발생 가능성이 높기 때문에 비추천&lt;/p&gt;</description>
      <category>시스템 관리/docker</category>
      <category>docker</category>
      <category>Failed to start docker.service</category>
      <category>Unit docker.service not found</category>
      <category>도커</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/51</guid>
      <comments>https://threemealsofcoding.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 5 Aug 2025 10:02:29 +0900</pubDate>
    </item>
    <item>
      <title>자바 클래스 로더와 WAS에서의 로딩 우선순위 &amp;mdash; 충돌 없이 이해하기</title>
      <link>https://threemealsofcoding.tistory.com/50</link>
      <description>&lt;h2 data-end=&quot;170&quot; data-start=&quot;151&quot; data-ke-size=&quot;size26&quot;&gt;클래스 충돌, 왜 발생할까요?&lt;/h2&gt;
&lt;p data-end=&quot;254&quot; data-start=&quot;172&quot; data-ke-size=&quot;size16&quot;&gt;Java 웹 애플리케이션을 개발하다 보면&lt;br /&gt;&quot;이 클래스 왜 안 불러와지지?&quot;, &quot;같은 이름인데 충돌이 나네?&quot;&lt;br /&gt;이런 현상을 자주 겪게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;379&quot; data-start=&quot;256&quot; data-ke-size=&quot;size16&quot;&gt;특히 WAS(Web Application Server)를 사용하는 경우, Java SE와는 다른 &lt;b&gt;클래스 로딩 구조&lt;/b&gt;를 가지고 있어서&lt;br /&gt;충돌이나 예외가 발생하는 원인을 이해하지 못하면 디버깅에 큰 어려움을 겪습니다.&lt;/p&gt;
&lt;p data-end=&quot;412&quot; data-start=&quot;381&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 다음 내용을 중심으로 개념을 정리해 봅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;536&quot; data-start=&quot;414&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;414&quot;&gt;Java 클래스 로딩 순서와 부모 위임 모델&lt;/li&gt;
&lt;li data-end=&quot;462&quot; data-start=&quot;441&quot;&gt;시스템 클래스 오버라이드 가능 여부&lt;/li&gt;
&lt;li data-end=&quot;488&quot; data-start=&quot;463&quot;&gt;WAS에서 클래스 우선순위가 달라지는 이유&lt;/li&gt;
&lt;li data-end=&quot;536&quot; data-start=&quot;489&quot;&gt;같은 클래스가 classes, JAR, WAS 모듈에 있을 때 어떤 게 로딩될까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;564&quot; data-start=&quot;543&quot; data-ke-size=&quot;size26&quot;&gt;Java의 기본 클래스 로딩 구조&lt;/h2&gt;
&lt;p data-end=&quot;686&quot; data-start=&quot;566&quot; data-ke-size=&quot;size16&quot;&gt;Java는 기본적으로 &lt;b&gt;부모 위임 모델 (Parent Delegation Model)&amp;nbsp;&lt;/b&gt; 을 사용합니다.&lt;br /&gt;즉, 클래스 로더가 클래스 로딩 요청을 받으면 &lt;b&gt;자기 자신이 처리하기 전에 부모에게 먼저 위임&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;704&quot; data-start=&quot;688&quot; data-ke-size=&quot;size23&quot;&gt;클래스 로딩 위임 순서&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;886&quot; data-start=&quot;706&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;757&quot; data-start=&quot;706&quot;&gt;Bootstrap ClassLoader (JDK의 핵심 클래스, java.lang 등)&lt;/li&gt;
&lt;li data-end=&quot;817&quot; data-start=&quot;758&quot;&gt;Platform ClassLoader (Java 9 이전에는 Extension ClassLoader)&lt;/li&gt;
&lt;li data-end=&quot;886&quot; data-start=&quot;818&quot;&gt;Application ClassLoader (classpath, WEB-INF/classes, lib/*.jar 등)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;946&quot; data-start=&quot;888&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부모가 클래스를 로딩할 수 있으면 자식은 로딩하지 않고, 이미 로드된 클래스를 그대로 사용합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;946&quot; data-start=&quot;888&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;976&quot; data-start=&quot;953&quot; data-ke-size=&quot;size26&quot;&gt;시스템 클래스는 오버라이드할 수 없다&lt;/h2&gt;
&lt;p data-end=&quot;1088&quot; data-start=&quot;978&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 내가 직접 만든 java/util/ArrayList.class 파일을 넣었다 하더라도,&lt;br /&gt;JVM은 해당 클래스를 &lt;b&gt;무조건 Bootstrap ClassLoader&lt;/b&gt;에서 로딩합니다.&lt;/p&gt;
&lt;p data-end=&quot;1164&quot; data-start=&quot;1090&quot; data-ke-size=&quot;size16&quot;&gt;사용자 정의 클래스는 무시되며,&lt;br /&gt;잘못하면 SecurityException, LinkageError 같은 예외가 발생합니다.&lt;/p&gt;
&lt;p data-end=&quot;1242&quot; data-start=&quot;1166&quot; data-ke-size=&quot;size16&quot;&gt;이는 JVM 보안 정책 때문이며,&lt;br /&gt;&lt;b&gt;java.*&lt;/b&gt; 또는 &lt;b&gt;javax.*&lt;/b&gt; 계열의 클래스는 절대로 오버라이드할 수 없습니다.&lt;/p&gt;
&lt;p data-end=&quot;1242&quot; data-start=&quot;1166&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1273&quot; data-start=&quot;1249&quot; data-ke-size=&quot;size26&quot;&gt;WAS에서는 클래스 로딩 방식이 다르다&lt;/h2&gt;
&lt;p data-end=&quot;1372&quot; data-start=&quot;1275&quot; data-ke-size=&quot;size16&quot;&gt;대부분의 WAS(Web Application Server)는 Java SE와는 다르게&lt;br /&gt;&lt;b&gt;클래스 로더 격리 정책(ClassLoader Isolation)&lt;/b&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-end=&quot;1482&quot; data-start=&quot;1374&quot; data-ke-size=&quot;size16&quot;&gt;즉, 부모 위임보다는 &lt;b&gt;애플리케이션 자체의 classes나 lib JAR 파일을 먼저 로딩&lt;/b&gt;합니다.&lt;br /&gt;이런 정책은 애플리케이션 간의 충돌을 방지하고, 서버 안정성을 높이기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1482&quot; data-start=&quot;1374&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1507&quot; data-start=&quot;1489&quot; data-ke-size=&quot;size26&quot;&gt;WAS 클래스 로딩 우선순위&lt;/h2&gt;
&lt;p data-end=&quot;1540&quot; data-start=&quot;1509&quot; data-ke-size=&quot;size16&quot;&gt;다음은 일반적인 WAS에서의 클래스 로딩 우선순위입니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1675&quot; data-start=&quot;1542&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1575&quot; data-start=&quot;1542&quot;&gt;WEB-INF/classes (애플리케이션 클래스)&lt;/li&gt;
&lt;li data-end=&quot;1613&quot; data-start=&quot;1576&quot;&gt;WEB-INF/lib/*.jar (애플리케이션 라이브러리)&lt;/li&gt;
&lt;li data-end=&quot;1650&quot; data-start=&quot;1614&quot;&gt;서버 공통 모듈 또는 공유 클래스 (예: WAS 내장 모듈)&lt;/li&gt;
&lt;li data-end=&quot;1675&quot; data-start=&quot;1651&quot;&gt;JDK 시스템 클래스 (java.*)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1759&quot; data-start=&quot;1677&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;같은 클래스가 서버 모듈과 애플리케이션 양쪽에 동시에 존재할 경우&lt;/b&gt;,&lt;br /&gt;WAS는 보통 &lt;b&gt;애플리케이션 쪽 클래스를 먼저 로딩&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-end=&quot;1759&quot; data-start=&quot;1677&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1785&quot; data-start=&quot;1766&quot; data-ke-size=&quot;size26&quot;&gt;충돌이 발생하는 대표적인 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1924&quot; data-start=&quot;1787&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1836&quot; data-start=&quot;1787&quot;&gt;ClassCastException: 같은 클래스인데 로더가 달라서 타입 호환이 안 됨&lt;/li&gt;
&lt;li data-end=&quot;1875&quot; data-start=&quot;1837&quot;&gt;LinkageError: 패키지 이름 중복, 클래스 정의 충돌 등&lt;/li&gt;
&lt;li data-end=&quot;1924&quot; data-start=&quot;1876&quot;&gt;SecurityException: 시스템 클래스를 잘못 오버라이드하려고 시도했을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1944&quot; data-start=&quot;1931&quot; data-ke-size=&quot;size26&quot;&gt;클래스 로딩 흐름도&lt;/h2&gt;
&lt;p data-end=&quot;2036&quot; data-start=&quot;1946&quot; data-ke-size=&quot;size16&quot;&gt;클래스 로딩이 실제로 어떤 경로를 따라 진행되는지를 시각적으로 이해하고 싶다면&lt;br /&gt;아래의 흐름도를 참고하세요.&lt;/p&gt;
&lt;p data-end=&quot;2036&quot; data-start=&quot;1946&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 Java SE WAS 환경&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;로딩 모델&lt;/td&gt;
&lt;td&gt;부모 위임&lt;/td&gt;
&lt;td&gt;애플리케이션 우선 (격리 정책)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시스템 클래스 오버라이드&lt;/td&gt;
&lt;td&gt;불가&lt;/td&gt;
&lt;td&gt;불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용자 클래스 오버라이드&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;가능 (보통 classes가 우선)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;충돌 가능성&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;중복 시 높음 (주의 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2299&quot; data-start=&quot;2268&quot; data-ke-size=&quot;size26&quot;&gt;WAS에서 시스템 모듈 클래스가 먼저 로딩되는 이유&lt;/h2&gt;
&lt;p data-end=&quot;2387&quot; data-start=&quot;2301&quot; data-ke-size=&quot;size16&quot;&gt;지금까지는 일반적인 로딩 우선순위를 다뤘지만, 실제 WAS 환경에서는 &lt;b&gt;서버 모듈 클래스가 먼저 로딩되어 lib 클래스가 무시되는 경우&lt;/b&gt;가 존재합니다.&lt;/p&gt;
&lt;p data-end=&quot;2422&quot; data-start=&quot;2389&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 WAS에는 다음과 같은 시스템 모듈이 존재합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2524&quot; data-start=&quot;2424&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2471&quot; data-start=&quot;2424&quot;&gt;org.jboss.logging.Logger &amp;rarr; WildFly 로깅 시스템&lt;/li&gt;
&lt;li data-end=&quot;2524&quot; data-start=&quot;2472&quot;&gt;javax.transaction.UserTransaction &amp;rarr; 서버 트랜잭션 모듈 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2658&quot; data-start=&quot;2526&quot; data-ke-size=&quot;size16&quot;&gt;이런 클래스들은 &lt;b&gt;WAS 부팅 시점에 시스템 모듈로 먼저 메모리에 로딩&lt;/b&gt;됩니다.&lt;br /&gt;이후 애플리케이션이 동일한 FQCN의 클래스를 lib 경로에 포함하더라도,&lt;br /&gt;&lt;b&gt;JVM은 이미 로딩된 클래스를 우선 사용하기 때문에 무시&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-end=&quot;2658&quot; data-start=&quot;2526&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2681&quot; data-start=&quot;2665&quot; data-ke-size=&quot;size26&quot;&gt;왜 이런 일이 발생할까?&lt;/h2&gt;
&lt;p data-end=&quot;2746&quot; data-start=&quot;2683&quot; data-ke-size=&quot;size16&quot;&gt;JVM은 같은 이름의 클래스(FQCN)가 이미 로딩되었으면,&lt;br /&gt;하위 로더에 있더라도 다시 로딩하지 않습니다.&lt;/p&gt;
&lt;p data-end=&quot;2871&quot; data-start=&quot;2748&quot; data-ke-size=&quot;size16&quot;&gt;그리고 WAS는 시스템 모듈을 **상위 클래스 로더(ModuleClassLoader)**에서 먼저 로딩하기 때문에,&lt;br /&gt;애플리케이션 레벨(lib/*.jar)은 &lt;b&gt;상위에 의해 이미 로딩된 클래스를 덮어쓸 수 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;2893&quot; data-start=&quot;2878&quot; data-ke-size=&quot;size26&quot;&gt;실제로 발생하는 문제들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3032&quot; data-start=&quot;2895&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2957&quot; data-start=&quot;2895&quot;&gt;slf4j, logback 등 로깅 설정이 무시되고, WAS 기본 로깅(jboss-logging)만 동작&lt;/li&gt;
&lt;li data-end=&quot;2993&quot; data-start=&quot;2958&quot;&gt;기대한 log4j 버전이 아닌 시스템 모듈 버전이 사용됨&lt;/li&gt;
&lt;li data-end=&quot;3032&quot; data-start=&quot;2994&quot;&gt;인터페이스 충돌로 인해 ClassCastException 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;2274&quot; data-start=&quot;2258&quot; data-ke-size=&quot;size26&quot;&gt;어떻게 대응하면 좋을까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2504&quot; data-start=&quot;2276&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2328&quot; data-start=&quot;2276&quot;&gt;같은 FQCN(전체 클래스 이름)을 가진 클래스가 여러 위치에 존재하지 않도록 관리하세요.&lt;/li&gt;
&lt;li data-end=&quot;2414&quot; data-start=&quot;2329&quot;&gt;서버 공용 모듈과 충돌 가능성이 있는 경우, WAS 설정 파일(deployment-structure.xml 등)을 통해 모듈 제외 처리를 고려하세요.&lt;/li&gt;
&lt;li data-end=&quot;2504&quot; data-start=&quot;2415&quot;&gt;로딩 순서나 위치를 정확히 확인하고 싶다면 -verbose:class 옵션이나 getClass().getClassLoader() 메서드를 활용하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;2517&quot; data-start=&quot;2511&quot; data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-end=&quot;2594&quot; data-start=&quot;2519&quot; data-ke-size=&quot;size16&quot;&gt;클래스 로더는 이해하기 어렵지만, 한 번 구조를 파악하고 나면&lt;br /&gt;WAS 환경에서 발생하는 많은 충돌 문제를 쉽게 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;2667&quot; data-start=&quot;2596&quot; data-ke-size=&quot;size16&quot;&gt;이 글이 클래스 로딩 우선순위를 이해하는 데 도움이 되었기를 바랍니다.&lt;/p&gt;</description>
      <category>프로그래밍 언어/JAVA</category>
      <category>java classloader</category>
      <category>Java EE</category>
      <category>JVM</category>
      <category>WAS</category>
      <category>백엔드 개발</category>
      <category>웹 애플리케이션 서버</category>
      <category>자바</category>
      <category>클래스 로딩</category>
      <category>클래스 충돌</category>
      <category>클래스로더</category>
      <author>코딩금융치료</author>
      <guid isPermaLink="true">https://threemealsofcoding.tistory.com/50</guid>
      <comments>https://threemealsofcoding.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 16 Jun 2025 16:33:39 +0900</pubDate>
    </item>
  </channel>
</rss>