<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>기록보관소</title>
    <link>https://brotherjeantech.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 8 May 2026 22:05:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>BestTomaTo</managingEditor>
    <item>
      <title>[SK 쉴더스 루키즈] Notion API 연동 실습</title>
      <link>https://brotherjeantech.tistory.com/62</link>
      <description>&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Notion Database&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션에는 데이터베이스가 있다. 데이터베이스에는 고유 ID가 있기 때문에, 파이썬 코드를 이용해 데이터베이스를 조작할 수 있다. 노션 애플리케이션에도 ID를 확인할 수 있지만, 웹을 통해 ID를 확인하는게 가장 정확한 것 같다.&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;실습에 사용한 것은 데이터베이스 ID, 데이터베이스 소스 ID를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CxVQf/dJMcah5plvZ/lw2AjKkNUk7PRDGAVopi6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CxVQf/dJMcah5plvZ/lw2AjKkNUk7PRDGAVopi6K/img.png&quot; data-alt=&quot;URL로 알아보는 데이터베이스 고유 ID&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CxVQf/dJMcah5plvZ/lw2AjKkNUk7PRDGAVopi6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCxVQf%2FdJMcah5plvZ%2Flw2AjKkNUk7PRDGAVopi6K%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;540&quot; height=&quot;44&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;URL로 알아보는 데이터베이스 고유 ID&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습에는 다음의 데이터베이스를 사용했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWJwYU/dJMcaja1VB0/KK5pJscN2MmRMX1BoKKOt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWJwYU/dJMcaja1VB0/KK5pJscN2MmRMX1BoKKOt0/img.png&quot; data-alt=&quot;상품 재고 관리 DB&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWJwYU/dJMcaja1VB0/KK5pJscN2MmRMX1BoKKOt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWJwYU%2FdJMcaja1VB0%2FKK5pJscN2MmRMX1BoKKOt0%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;600&quot; height=&quot;153&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상품 재고 관리 DB&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파란색 New 메뉴 바로 옆에 ID를 확인할 수 있는 버튼이 있으니 참고하길 바란다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Notion API와 JSON&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션은 코드로 접속할 수 있도록 API 명세서를 docs로 적어 놓고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.notion.com/guides/get-started/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.notion.com/guides/get-started/overview&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778074739183&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Overview - Notion Docs&quot; data-og-description=&quot;Discover what Notion connections are, when to use each type, and what you can build.&quot; data-og-host=&quot;developers.notion.com&quot; data-og-source-url=&quot;https://developers.notion.com/guides/get-started/overview&quot; data-og-url=&quot;https://developers.notion.com/guides/get-started/overview&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bilp5W/dJMb8SpLYx0/3Tby7LHqxk2EjDkk7OhSy0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bLiNBj/dJMb9bv6gd6/xWRb7N3j2kqel1pqkE2lCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/du5Lxx/dJMb9cBL3aY/rXmDDNEgD2R2OLQvT1XzE0/img.jpg?width=1800&amp;amp;height=1200&amp;amp;face=0_0_1800_1200&quot;&gt;&lt;a href=&quot;https://developers.notion.com/guides/get-started/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.notion.com/guides/get-started/overview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bilp5W/dJMb8SpLYx0/3Tby7LHqxk2EjDkk7OhSy0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bLiNBj/dJMb9bv6gd6/xWRb7N3j2kqel1pqkE2lCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/du5Lxx/dJMb9cBL3aY/rXmDDNEgD2R2OLQvT1XzE0/img.jpg?width=1800&amp;amp;height=1200&amp;amp;face=0_0_1800_1200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Overview - Notion Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover what Notion connections are, when to use each type, and what you can build.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.notion.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 데이터베이스에서 데이터를 조회하는 HTTP 명령 구조이다. 파이썬 코드를 이용해 다음과 같은 HTTP 패킷 구조를 만들고 전송하면 데이터베이스 값을 받아볼 수 있다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Notion 데이터베이스 연동 실습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원소 조회하기의 핵심은 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. .env 파일을 이용해 통신할 때 필요한 Token과 DB_ID를 안전하게 관리하기.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. Notion API Docs에 나와 있는 대로 requests header를 미리 설계한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. try - catch 구문을 이용해 오류를 잡아낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. response는 대부분 JSON이다. JSON은 기본적으로 Dict와 유사하므로 키값를 잘 판단해 원하는 값을 도출한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JNXCG/dJMcagyCENA/QvV4JI3Fu2N3YENK76pMX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JNXCG/dJMcagyCENA/QvV4JI3Fu2N3YENK76pMX1/img.png&quot; data-alt=&quot;조회한 결과값&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JNXCG/dJMcagyCENA/QvV4JI3Fu2N3YENK76pMX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJNXCG%2FdJMcagyCENA%2FQvV4JI3Fu2N3YENK76pMX1%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;540&quot; height=&quot;158&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;조회한 결과값&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1778075499375&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import os
import requests
from dotenv import load_dotenv

load_dotenv()

NOTION_TOKEN = os.getenv(&quot;NOTION_KEY&quot;)
DB_ID = os.getenv(&quot;DATABASE_ID&quot;)

if not NOTION_TOKEN or not DB_ID:
    raise ValueError(&quot;TOKEN 값 없음!&quot;)

url = f&quot;https://api.notion.com/v1/databases/{DB_ID}/query&quot;

header = {
    &quot;Authorization&quot;: f&quot;Bearer {NOTION_TOKEN}&quot;,
    &quot;Notion-Version&quot;: &quot;2022-06-28&quot;,
    &quot;Content-Type&quot;: &quot;application/json&quot;
}

try:
    response = requests.post(url, headers=header, timeout=10)
    response.raise_for_status()

    data = response.json()
    page_list = data.get(&quot;results&quot;)

    for page in page_list:
        props = page.get(&quot;properties&quot;)
        price = props.get(&quot;금액&quot;, {}).get(&quot;number&quot;, 0)
        qty = props.get(&quot;재고 수량&quot;, {}).get(&quot;number&quot;, 0)
        total_price = props.get(&quot;총금액&quot;, {}).get(&quot;formula&quot;, {}).get(&quot;number&quot;, 0)
        
        title = props.get(&quot;상품명&quot;, {}).get(&quot;title&quot;, [])
        name = title[0].get(&quot;text&quot;, {}).get(&quot;content&quot;, &quot;Error&quot;)

        print(price, qty, total_price, name)

except Exception as e:
    print(e)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778075624971&quot; class=&quot;scilab&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;curl -X POST https://api.notion.com/v1/data_sources/248104cd477e80afbc30000bd28de8f9/query \
  -H 'Authorization: Bearer '&quot;$NOTION_API_KEY&quot;'' \
  -H &quot;Content-Type: application/json&quot; \
  -H &quot;Notion-Version: 2026-03-11&quot; \
	--data '{
	  &quot;filter&quot;: {
      &quot;property&quot;: &quot;Last ordered&quot;,
      &quot;date&quot;: {
        &quot;past_week&quot;: {}
      }
		}
	}'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;원소 생성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 내에 원소를 생성하기 위해선 &lt;b&gt;parents&lt;/b&gt; 데이터와 &lt;b&gt;properties&lt;/b&gt; 데이터가 필요하다. parents 데이터는 원소를 넣을 데이터베이스 ID다. properties는 데이터베이스 안에 집어 넣을 데이터의 형식이다.&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;공식 문서에서 나온 curl 요청 형식에 오류가 있었다. 공식 문서에서 지정하는 parent 형식으로 맞췄는데, parent JSON 안에 있는 &quot;database_source_id&quot; 키는 특수한 상황에서만 동작하는 키 형식이라 오류가 계속 났었다. 이 키 대신 &quot;database_id&quot; 키를 사용하니 문제 없이 JSON 파싱이 이루어졌다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;최신 버전의 경우 제시한 방법이 안될 수도 있으니 유의하자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원소 생성하기의 핵심은 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 원소 조회하기와 코드 구조는 같다. 다만 생성하고자 하는 원소의 정보를 payload로 구현해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. payload를 구현할 때는 데이터베이스의 구조와 일치해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHzOZY/dJMcahEilCO/UE8PgRKPvTxvo49XfO0iuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHzOZY/dJMcahEilCO/UE8PgRKPvTxvo49XfO0iuK/img.png&quot; data-alt=&quot;생성한 결과값&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHzOZY/dJMcahEilCO/UE8PgRKPvTxvo49XfO0iuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHzOZY%2FdJMcahEilCO%2FUE8PgRKPvTxvo49XfO0iuK%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;540&quot; height=&quot;52&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성한 결과값&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1778077191166&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import requests
from dotenv import load_dotenv

load_dotenv()

NOTION_TOKEN = os.getenv(&quot;NOTION_KEY&quot;)
DB_ID = os.getenv(&quot;DATABASE_ID&quot;)
DB_SOURCE_ID = os.getenv(&quot;DATABASE_SOURCE_ID&quot;)

if not NOTION_TOKEN or not DB_ID:
    raise ValueError(&quot;TOKEN 값 없음!&quot;)

url = f&quot;https://api.notion.com/v1/pages&quot;

header = {
    &quot;Authorization&quot;: f&quot;Bearer {NOTION_TOKEN}&quot;,
    &quot;Content-Type&quot;: &quot;application/json&quot;,
    &quot;Notion-Version&quot;: &quot;2026-03-11&quot;
}

payload = {
    &quot;parent&quot; : {
        &quot;database_id&quot; : DB_ID
    },
    &quot;properties&quot; : {
        &quot;상품명&quot; : {
            &quot;type&quot; : &quot;title&quot;,
            &quot;title&quot; : [ {&quot;type&quot; : &quot;text&quot;, &quot;text&quot; : {&quot;content&quot; : &quot;BBQ 황금올리브치킨&quot;}} ]
        },
        &quot;재고 수량&quot; : {
            &quot;type&quot; : &quot;number&quot;,
            &quot;number&quot; : 23
        },
        &quot;금액&quot; : {
            &quot;type&quot; : &quot;number&quot;,
            &quot;number&quot; : 23000
        }
    }
}

try:
    response = requests.post(url, headers=header, json=payload, timeout=10)
    data = response.json()
    print(data)
except Exception as e:
    print(e)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3️⃣ 원소 정렬 및 필터링 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion Query API를 이용해 데이터베이스 원소들의 관계를 변경하여 조회할 수 있다. 원소 생성하기 때 requests에 생성하고자 하는 데이터를 JSON으로 전달한 것과 마찬가지로, 보고자 하는 원소들의 관계를 payload로 생성해 전달한다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;쿼리 API이기 때문에 데이터베이스의 원본은 바뀌지 않고 정렬된 값만 조회 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IIX2n/dJMcaakS3uY/78Bgbok0mJ2INJ8DCRjIuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IIX2n/dJMcaakS3uY/78Bgbok0mJ2INJ8DCRjIuk/img.png&quot; data-alt=&quot;정렬한 데이터베이스 원소값&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IIX2n/dJMcaakS3uY/78Bgbok0mJ2INJ8DCRjIuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIIX2n%2FdJMcaakS3uY%2F78Bgbok0mJ2INJ8DCRjIuk%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;540&quot; height=&quot;141&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;정렬한 데이터베이스 원소값&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1778078441366&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import requests
from dotenv import load_dotenv

load_dotenv()

NOTION_TOKEN = os.getenv(&quot;NOTION_KEY&quot;)
DB_ID = os.getenv(&quot;DATABASE_ID&quot;)

if not NOTION_TOKEN or not DB_ID:
    raise ValueError(&quot;TOKEN 값 없음!&quot;)

url = f&quot;https://api.notion.com/v1/databases/{DB_ID}/query&quot;

header = {
    &quot;Authorization&quot;: f&quot;Bearer {NOTION_TOKEN}&quot;,
    &quot;Notion-Version&quot;: &quot;2022-06-28&quot;,
    &quot;Content-Type&quot;: &quot;application/json&quot;
}

try:
    response = requests.post(url, headers=header, timeout=10)
    response.raise_for_status()

    data = response.json()
    page_list = data.get(&quot;results&quot;)

    for page in page_list:
        props = page.get(&quot;properties&quot;)
        price = props.get(&quot;금액&quot;, {}).get(&quot;number&quot;, 0)
        qty = props.get(&quot;재고 수량&quot;, {}).get(&quot;number&quot;, 0)
        total_price = props.get(&quot;총금액&quot;, {}).get(&quot;formula&quot;, {}).get(&quot;number&quot;, 0)
        
        title = props.get(&quot;상품명&quot;, {}).get(&quot;title&quot;, [])
        name = title[0].get(&quot;text&quot;, {}).get(&quot;content&quot;, &quot;Error&quot;)

        print(price, qty, total_price, name)

except Exception as e:
    print(e)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;소감&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬과 API를 이용해 다른 웹 애플리케이션의 데이터를 바꾸는 것이 흥미롭다. 이런 것들을 연습해서 생산성을 높이는 나만의 코딩 환경도 구축할 수 있을 것 같다.&lt;/p&gt;</description>
      <category>Security/SK Shieldus Rookies</category>
      <category>DATABASE</category>
      <category>Notion</category>
      <category>requests</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/62</guid>
      <comments>https://brotherjeantech.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 6 May 2026 22:42:04 +0900</pubDate>
    </item>
    <item>
      <title>[SK 쉴더스 루키즈] Python 문법 1</title>
      <link>https://brotherjeantech.tistory.com/61</link>
      <description>&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Python venv&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;where python
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 설치되어 있는 파이썬의 위치를 확인, 가장 위에 있는 애가 현재 실행되고 있는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;python -m venv venv(가상환경 이름)
&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;pip : python install package
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pip list : 현재 설치되어 있는 파이썬 패키지 (venv에서도 보인다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원래는 venv 폴더 안에 .env, .gitignore를 두고 파이썬 코드를 작성하는게 맞다.
&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;pycache, venv 때문에 협업할 때 오류가 많이 났었다. 이런건 미리 세팅해서 빼주도록 하자.&lt;/li&gt;
&lt;li&gt;shift + 우클릭 : 터미널 선택 메뉴 창&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTCJkJ/dJMcahjYNoR/AHApwdEjuN3t34Bpcixp4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTCJkJ/dJMcahjYNoR/AHApwdEjuN3t34Bpcixp4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTCJkJ/dJMcahjYNoR/AHApwdEjuN3t34Bpcixp4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTCJkJ%2FdJMcahjYNoR%2FAHApwdEjuN3t34Bpcixp4k%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;540&quot; height=&quot;304&quot; data-origin-width=&quot;1133&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;python extension을 활용하면 그냥 재생 버튼만 눌러도 실행이 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;extension 안에 인터프리터도 있구나.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;import os
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영체제를 사용할 수 있는 파이썬 라이브러리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;.env
&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;*/.env &amp;larr; 이렇게 적으면 각각의 파일 속에 있는 .env 파일을 gitignore하라는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;requirement.txt
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pip freeze &amp;gt; requirements.txt
&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;이래서 해커톤 때 requirements.txt를 작성하라고 했던 거구나.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pip install -r requirements.txt
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 명령어를 작성하면 requirements.txt에 있는 라이브러리를 그대로 다운 받아 준다.&lt;/li&gt;
&lt;li&gt;진짜 대박 명령어!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정리하자면
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;venv를 설정한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ctrl + shift + P &amp;rArr; venv를 자동으로 설정할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;venv를 실행한다.&lt;/li&gt;
&lt;li&gt;pip install -r requirements.txt를 실행해 라이브러리를 다 다운 받아라.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Python 기본 문법&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;개꿀팁
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;alt 키 누르고 이동하면 줄 그대로 이동한다.&lt;/li&gt;
&lt;li&gt;ctrl + alt 다중 커서&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실무랑 연결지어서 이해해보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정수는 IP 번호를 적을 때 사용한다.&lt;/li&gt;
&lt;li&gt;부동소수점 : CPU 사용률, 가용성 수치&lt;/li&gt;
&lt;li&gt;불리언 : 서버 On/Off 유무&lt;/li&gt;
&lt;li&gt;문자열 : 대부분의 데이&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;부동 소수점(float)을 다루다 보면 근사의 위험을 항상 생각해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2/3 == 0.6666666666666666&lt;/li&gt;
&lt;li&gt;(5/3)-1 == 0.6666666666666667&lt;/li&gt;
&lt;/ul&gt;
&amp;rArr; 코드 작성 시 항상 유의해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱싱은 하나의 문자를 찍어주는 것, 슬라이싱은 문자의 범위를 찍어주는 것.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;str, list, tuple, range 얘네들은 순서가 있기 때문에 인덱싱과 슬라이싱이 된다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;문자열을 인덱싱을 하면 &lt;b&gt;한 글자의 문자열&lt;/b&gt;이 된다.&lt;/li&gt;
&lt;li&gt;[-1]은 맨 뒤 글자를 나타낸다.&lt;/li&gt;
&lt;li&gt;슬라이싱에서는 [시작:끝+1]이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[5:8] 아! 8-5=3 3개 가져오는거네. 5에서 3개를 가져오는 거네~ 이렇게 이해하자.&lt;/li&gt;
&lt;li&gt;[0:9] 아! 9글자 가져오는 거네.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;print(sing[:-1])
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이거는 맨 뒤 글자는 빠진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sing = &quot;동해물과 백두산이 마르고 닳도록&quot;

# 인덱스[번째]
animal = sing[0] + sing[2] 

print(animal)
print(&quot;후지&quot; + sing[-1] + &quot;페스티벌&quot;)

# 슬라이싱: 범위
print(sing[5:8])
print(sing[:9])
print(sing[:])
print(sing[5:])
print(sing[:8])
print(sing[:-1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;split()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정한 기준을 정하면 그거에 맞춰 문자열을 쪼갠다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쪼갠 값들은 리스트에 들어가게 된다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;message = &quot;위험-사용자인증실패-2026-04-27&quot;

message_list = message.split(&quot;-&quot;)
risky_level = message_list[0]
real_msg = message_list[1]
date_list = message_list[2:]

print(risky_level, real_msg, date_list)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;strip()
&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;lstrip, rstrip으로 왼쪽 오른쪽을 제거할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;names = &quot;  최인규, 유유엘, 신지혜, 남보라, 민병연\\n김명성, 김성현, 김수정, 서지우, 김태현\\n한서현  &quot;
print(names)
print(names.strip())
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;find(), index()
&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;find()는 원하는 문자가 없으면 -1을 반환한다.&lt;/li&gt;
&lt;li&gt;index()는 원하는 문자가 없으면 에러를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;문자열에 포함된 모든 | 문자의 개수(공백, 특수문자 | 포함)를 반환&quot;

# find(), index() 
print(text.find(&quot;포함된&quot;))
print(text[5:8]) =&amp;gt; &quot;포함된&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;replace(&amp;rdquo;,&amp;rdquo;, &amp;ldquo; &amp;ldquo;)
&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;join()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;os 경로 만들 때 가장 많이 사용한다. 편하니까.&lt;/li&gt;
&lt;li&gt;특정 문자 기준으로 대상 문자를 붙인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;python_path = &quot;Users/Administrator/infra_python32/.venv/Scripts&quot;
print(python_path.split(&quot;/&quot;))
python_path_list = python_path.split(&quot;/&quot;)
print(&quot;/&quot;.join(python_path_list))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;str 작은 따옴표, 큰 따옴표
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;python에서는 둘은 같은 string을 만들어 낸다!&lt;/li&gt;
&lt;li&gt;만약 &amp;lsquo; 작은 따옴표를 문자열 안에서 표현하고 싶다면 역슬래시를 사용한다. \&amp;rsquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;carraige_return
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\r&lt;/li&gt;
&lt;li&gt;HTTP에서 나오는 거긴 한데 뭔지 알아보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;f-string
&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;f-string이 강한 이유는 마스킹, 정렬 연산이 가능하기 때문이다.&lt;/li&gt;
&lt;li&gt;(참고) %d decimal %f float&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;input
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 함수, 입력을 받으면 문자열로 입력 받는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원치 않는 다면 int() 타입 변환을 해야 한다.&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;kor_scrore = int(input(&amp;rdquo;당신의 점수는? : &amp;ldquo;))&lt;/li&gt;
&lt;li&gt;kor_str = input(&amp;rdquo;당신의 점수는? : )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;random&lt;/li&gt;
&lt;li&gt;type(), dir()&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# type() -&amp;gt; 타입 찾기

# print(type(1))
# print(type(1.1))
# print(type(&quot;전형진&quot;))
# print(type(False))
# print(type(True))
# print(type(0))
# print(type(None))

# dir() -&amp;gt; 가진 재산 확인
# 이 클래스의 메소드를 전부 확인하게 해준다. 상속 관계는 안보이는 것 같다.
'''
__len__, replace의 차이
- 언더바가 있는 것은 len() 단독 함수로 간다.
- 언더바가 없는 것은 .replace() 이렇게 간다. 아마 직접 달려 있는 메소드라서 그런가보다.

'''

# print(dir(int))
print(dir(str))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;len 함수는 모든 자료형에 다 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Python 자료구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Immutable&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list, tuple, set, dict, range, string&amp;hellip;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서, 수정, 중복 특징 위주로 외워야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list : 순서 있다, 수정할 수 있다, 중복 가능하다.&lt;/li&gt;
&lt;li&gt;tuple : 순서 있다, 수정할 수 없다, 중복 가능하다.&lt;/li&gt;
&lt;li&gt;set : 순서 없다, 수정할 수 있다. 중복 불가능하다.&lt;/li&gt;
&lt;li&gt;dict : key-value 쌍, 순서 생겼다, 수정할 수 있다. key는 중복 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PBL 1번은 for문 없이 1일차 + 2일차 약간으로 해결할 수 있다. 해결해 보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;리스트
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append : 원소 하나 추가&lt;/li&gt;
&lt;li&gt;remove : 리스트는 중복이 허용되는데 remove는 원소 하나만 지우나? 맞다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서는 어떻게 지워지는가? 맨 앞에 있는게 지워진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;extend
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iterable한 객체를 포함시킬 수 있다.&lt;/li&gt;
&lt;li&gt;반환값을 갖지 않고 바로 리스트에 대입된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;insert
&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;server_list.extend([&quot;WEB-02&quot;])
# server_list = server_list + [&quot;WEB-02&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 내가 원하는 메소드가 없으면 내가 조합해서 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;튜플
&lt;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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;셋
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;셋 생성 및 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;변경이 안된다는 것은 안의 값은 수정이 안된다는 것이 새로운 값은 추가가 가능하다!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;순서 없기 때문에 인덱싱 안됨!&lt;/li&gt;
&lt;li&gt;각종 집합 연산
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;intersection 교집합&lt;/li&gt;
&lt;li&gt;difference 차집합&lt;/li&gt;
&lt;li&gt;union 합집합&lt;/li&gt;
&lt;li&gt;_update는 원본 집합을 직접 수정, 갱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;burger_set = set()
burger_set.add(&quot;싸이버거 세트&quot;)
print(burger_set)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;dict
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 - 값 쌍, 실무에서는 dictionary가 제일 중요하다!&lt;/li&gt;
&lt;li&gt;collect = {}, 빈 중괄호는 빈 dict로 인식한다. set가 아니다.&lt;/li&gt;
&lt;li&gt;.get을 이용해 없는 원소에 대해 내가 원하는 값을 넣어줄 수 있거나 None으로 에러 없이 출력하게 할 수 있다. 실무에선 get을 사용하는 것을 추천한다.&lt;/li&gt;
&lt;li&gt;keys
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키를 합쳐서 dict_keys라는 새로운 자료형으로 반환한다.&lt;/li&gt;
&lt;li&gt;이후에 list로 바꾸어 인덱싱 및 슬라이싱을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;values
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값을 모아서 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;items
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 - 값 쌍을 튜플의 형식으로 묶어서 반환한다. for문을 이용해 활용하는게 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(server_info.get(&quot;password_list&quot;))
print(server_info.get(&quot;password_list&quot;, &quot;비밀~&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션 공통
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;in, not in
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열도 순서가 있으니 시퀀스고 컬렉션 함수를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;max min sum : 문자도 우선순위가 있다. 아스키 코드&lt;/li&gt;
&lt;li&gt;sorted
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬하고 반환까지 한다. &lt;b&gt;원본이 훼손되지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;원본을 훼손하는 것을 지양해야 한다. 훼손되면 안된다~&amp;rsquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;any
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요소들 중 하나라도 True이면 True 반환
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위험 요소가 하나라도 있으면 True 반환!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;all
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요소들 전부 True이어야 True 반환
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비밀번호 규칙은 전부 만족해야 하기 때문에 all!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Security/SK Shieldus Rookies</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/61</guid>
      <comments>https://brotherjeantech.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 29 Apr 2026 17:36:47 +0900</pubDate>
    </item>
    <item>
      <title>[SK 쉴더스 루키즈] Git &amp;amp; Github</title>
      <link>https://brotherjeantech.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Git의 기본 개념을 알아보고 Git과 Linux의 명령어를 알아보자.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;기본 Linux 명령어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pwd : 현재 경로&lt;/li&gt;
&lt;li&gt;start . : 현재 디렉토리를 window 탐색기로 열기&lt;/li&gt;
&lt;li&gt;mkdir : 디렉토리 생성&lt;/li&gt;
&lt;li&gt;rm :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rm -rf : recursively(재귀적으로, 하위 폴더까지 순차적으로 삭제) force(다른 사용자가 있어도 강제 삭제)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ls :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ls -al : 숨긴 항목까지 리스트화해서 보여라&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;touch [파일 이름] : 파일 생성&lt;/li&gt;
&lt;li&gt;cat [파일 이름] : 파일 내용 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Git의 기본(스테이지)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Git은 버저닝을 위한 툴이다. 프로젝트의 규모가 커지면 코드를 수정하기 전 기록을 남기고 싶은데, 과거의 정보가 없이 프로젝트를 진행하다 보면 이를 수정하고 관리하는데 힘들어지기 때문이다. 이를 위해 개발자들은 '버전'이라는 개념을 만들어 과거의 정보를 저장하기 시작했다.&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;Git은 이러한 '버전'을 남기는데 도와주는 시스템으로 버전 관리 시스템을 이용하면 버전관리는 물론 변경점관리, 백업 및 복구, 협업에 큰 이점이 있다.&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;Git의 로컬 저장소에는 working tree(working directory), stage(staging area, index), repository라는 3개의 공간이 존재한다. 각각이 어떤 역할을 하는지, 왜 이런 단계를 만들었는지 알아보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRadGJ/dJMcagSQACH/s6ObO6K9Q1C4OaOrwAKu21/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRadGJ/dJMcagSQACH/s6ObO6K9Q1C4OaOrwAKu21/img.jpg&quot; data-alt=&quot;Git Stage&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRadGJ/dJMcagSQACH/s6ObO6K9Q1C4OaOrwAKu21/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRadGJ%2FdJMcagSQACH%2Fs6ObO6K9Q1C4OaOrwAKu21%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;250&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Git Stage&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;working tree(working directory) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;working tree는 개발자가 작업하는 공간이다. &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;개발하고 있는 소스 파일이 위치해 있는 폴더(directory)라고 생각하면 된다. 우리가 코드를 수정하고, 새로운 파일이나 폴더를 생성하는 것 모두 이 작업 디렉터리에서 일어나는 일이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;stage(staging area)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 버전으로 들어갈 후보 파일들을 담아 놓는 공간이다. 무언가를 만들기 전 공구들을 준비해 놓는 작업대이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git add 명령어로 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;repository&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 버전으로 담긴 파일들을 보관하는 공간이다. 그 순간을 포착한다는 의미 해서 파일들의 스냅샷을 저장해 놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git commit 명령어로 진행한다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Git 기본 명령어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git status&lt;/b&gt;&amp;nbsp;
&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;On branch main

No commits yet

Untracked files:
  (use &quot;git add &amp;lt;file&amp;gt;...&quot; to include in what will be committed)
        story.txt

nothing added to commit but untracked files present (use &quot;git add&quot; to track)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 보면 깃이 추적하는 파일이 없다고 나온다. 추적하게 하려면 git add를 이용해 working directory에 넣어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git add&amp;nbsp;&lt;/b&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;staging, 로컬 저장소에 저장을 위한 사전 저장&lt;/li&gt;
&lt;li&gt;rm
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 생긴 파일은 rm &amp;mdash;cached로 add 한 거를 삭제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;restore
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 추적되기 시작한 파일은 restore로 add한거를 삭제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;vhdl&quot;&gt;&lt;code&gt;$ git status
On branch main

No commits yet

Changes to be committed:
  (use &quot;git rm --cached &amp;lt;file&amp;gt;...&quot; to unstage)
        new file:   story.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;vhdl&quot;&gt;&lt;code&gt;$ git status
On branch main
Changes to be committed:
  (use &quot;git restore --staged &amp;lt;file&amp;gt;...&quot; to unstage)
        modified:   story.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git commit&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-m &amp;ldquo;&amp;rdquo; : 커밋 메시지를 설정하고, 스테이징 된 파일들을 커밋한다(하나의 스냅샷, 버전으로 만듦)&lt;/li&gt;
&lt;li&gt;git commit -am :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 추적되기 시작한 파일은 add와 commit을 동시에 할 수 있다.&lt;/li&gt;
&lt;li&gt;겁나 편하다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;$ git commit -am &quot;second commit&quot;
[main 0b56cc9] second commit
 1 file changed, 2 insertions(+), 2 deletions(-)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git diff&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일의 변경 사항을 보여줘라&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;diff&quot;&gt;&lt;code&gt;diff --git a/story.txt b/story.txt
index 72547bc..494b518 100644
--- a/story.txt
+++ b/story.txt
@@ -1,4 +1,4 @@
 첫 만남은 너무 어려워
-계획대로 되는 게 업서서
+계획대로 되는 게 없어서
 첫 만남은 너무 어려워
 내 이름은 말야
\\ No newline at end of file
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git log&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;git log --oneline&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ git log
commit 0b56cc9b866407510418c11fa3b3daa1bd552617 (HEAD -&amp;gt; main)
Author: BestTomaTo &amp;lt;spacejin2000@naver.com&amp;gt;
Date:   Fri Apr 24 15:06:18 2026 +0900

    second commit

commit e14c269549e45800c66438ad8b75e61c7621d99d
Author: BestTomaTo &amp;lt;spacejin2000@naver.com&amp;gt;
Date:   Fri Apr 24 14:58:30 2026 +0900

    first commit
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;0b56cc9 (HEAD -&amp;gt; main) second commit
e14c269 first commit
&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Git 심화 명령어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git reset&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리셋 명령어는 버저닝을 하는 과정에서 굉장히 중요한 명령어다. 이 리셋에도 3가지 종류의 명령어가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csJUHW/dJMcaaZobLM/6Kr3NHJ4WmGk0YRyZeohRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csJUHW/dJMcaaZobLM/6Kr3NHJ4WmGk0YRyZeohRK/img.png&quot; data-alt=&quot;git reset 명령어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csJUHW/dJMcaaZobLM/6Kr3NHJ4WmGk0YRyZeohRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsJUHW%2FdJMcaaZobLM%2F6Kr3NHJ4WmGk0YRyZeohRK%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;540&quot; height=&quot;304&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;git reset 명령어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git reset &amp;mdash;soft : 커밋하기 전으로 (파일 수정 + add는 남아 있음, 커밋 메세지 수정)&lt;/li&gt;
&lt;li&gt;git reset &amp;mdash;mixed : 커밋과 add 하기 전으로 (파일 수정만 남아 있음)&lt;/li&gt;
&lt;li&gt;git reset &amp;mdash;hard : 파일도 아예 수정이 안되어 있는 수준으로 되돌린다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git reset &amp;mdash;option [커밋 ID]&lt;/li&gt;
&lt;li&gt;[커밋 ID]는 되돌아가고자 하는 커밋 ID를 입력한다. 삭제하고자 하는 게 아니라.&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git checkout&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;$ git checkout 0b20c9d
Note: switching to '0b20c9d'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c &amp;lt;new-branch-name&amp;gt;

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 0b20c9d T
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git checkout 명령어는 내가 있는 브랜치를 확인하는 명령어이다. 위 코드의 내용은 0b20c9d 커밋에서 하나의 브랜치를 뽑아나간다는 소리다. 이건 나중에 main 브랜치에 합쳐질 수도 있고 브랜치 자체가 사라질 수 있다. 기능 개발과 협업을 원활하게 하기 위해 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DWY4X/dJMcahK0qbZ/b4qKXZ3ziibVkTlEQVhMjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DWY4X/dJMcahK0qbZ/b4qKXZ3ziibVkTlEQVhMjK/img.png&quot; data-alt=&quot;커밋 ID 값이 0b20c9d에서 분기가 시작된 브랜치 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DWY4X/dJMcahK0qbZ/b4qKXZ3ziibVkTlEQVhMjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDWY4X%2FdJMcahK0qbZ%2Fb4qKXZ3ziibVkTlEQVhMjK%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;483&quot; height=&quot;187&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커밋 ID 값이 0b20c9d에서 분기가 시작된 브랜치 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git push -u&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git push &amp;mdash;set-upstream origin main와 같다!&lt;/li&gt;
&lt;li&gt;로컬 저장소 커밋 트리를 원격 저장소 커밋 트리와 연결하는 작업이다.&lt;/li&gt;
&lt;li&gt;로컬 저장소와는 달리 원격 저장소에 이미 커밋이 진행됐을 수도 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git clone 주소&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;li&gt;이걸 몰라서 맨날 폴더 2개 생겼다.&lt;/li&gt;
&lt;li&gt;만약 다른 곳에서 클론하고 다시 원격저장소로 올릴 때는 토큰이나 로그인이 반드시 필요하다.&lt;/li&gt;
&lt;li&gt;만약 .gitignore로 빠진 파일이 있다면 다시 넣어줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git merge 대상 브랜치&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; 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;github을 토큰으로 연결할 때는 리눅스 서버에서 터미널로 할 때 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제 아이디, 비밀번호는 막히기 때문에 이걸 써야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vscode 편집기로 git 명령어 없이 github 이용하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽 메뉴의 Source Control 메뉴를 확인하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;.gitignore 파일 활용하기&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vscode에서는 &amp;lsquo;U&amp;rsquo; 자가 사라진다. 아예 트래킹을 안 한다는 뜻이다.&lt;/li&gt;
&lt;li&gt;추적하기 전에 막는 게 제일 중요하다.&lt;/li&gt;
&lt;li&gt;pw.txt를 안 올라가게 막았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Security/SK Shieldus Rookies</category>
      <category>GIT</category>
      <category>github</category>
      <category>SK 쉴더스</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/60</guid>
      <comments>https://brotherjeantech.tistory.com/60#entry60comment</comments>
      <pubDate>Tue, 28 Apr 2026 13:19:57 +0900</pubDate>
    </item>
    <item>
      <title>[CMUX &amp;amp; AIM 해커톤] 해커톤 후기</title>
      <link>https://brotherjeantech.tistory.com/59</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2879&quot; data-origin-height=&quot;1639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsCbJs/dJMcadIwNIA/oVQC4Nko7lCL4kQUuimcMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsCbJs/dJMcadIwNIA/oVQC4Nko7lCL4kQUuimcMK/img.png&quot; data-alt=&quot;CMUX x AIM 해커톤&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsCbJs/dJMcadIwNIA/oVQC4Nko7lCL4kQUuimcMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsCbJs%2FdJMcadIwNIA%2FoVQC4Nko7lCL4kQUuimcMK%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;540&quot; height=&quot;307&quot; data-origin-width=&quot;2879&quot; data-origin-height=&quot;1639&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CMUX x AIM 해커톤&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMUX &amp;amp; AIM 해커톤에 참여했다. AI 해커톤은 처음이었다. 전에 웹 개발 해커톤에 참여한 적이 있었는데 AI 해커톤은 어떨지 궁금했고 AI를 어떻게 써야 하나 고민도 많았다. 끝나고 보니 많은 걸 배울 수 있었어 좋았던 것 같다. 현직자분들은 AI를 어떻게 쓰는지 보고 배울 수 있었고, 어떻게 하면 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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;CMUX와 AIM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 해커톤은 CMUX와 AIM의 후원이 있었다. 처음 들어보는 기업이라 찾아봤다.&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;cmux는 여러 터미널에서 독립적으로 돌리는 AI 에이전트들을 하나의 프로그램 안에서 편하게 관리할 수 있는 툴이다. 이번 해커톤에서 cmux를 사용하는 사람들을 봤는데 여러 개의 AI 터미널을 동시에 사용할 때 굉장히 편해보였다(macOS만 사용할 수 있어서 아쉽게도 이번 기회에 사용하진 못했다.). 팀원 중에 cmux를 사용하는 분이 있어서 옆에서 구경했는데 바이브 코딩이나 여러 개의 다른 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;AIM은 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;&lt;a href=&quot;https://cmux.com/ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://cmux.com/ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777215104140&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;cmux &amp;mdash; 멀티태스킹을 위해 만든 터미널&quot; data-og-description=&quot;AI 코딩 에이전트를 위한 네이티브 macOS 터미널. Claude Code, Codex, OpenCode, Gemini CLI, Kiro, Aider 등 모든 CLI 도구와 호환.&quot; data-og-host=&quot;cmux.com&quot; data-og-source-url=&quot;https://cmux.com/ko&quot; data-og-url=&quot;https://cmux.com/ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cyWVZz/dJMb8YpYbqv/OQriXnUGkO7NoHOkIuyyZ0/img.png?width=3840&amp;amp;height=2224&amp;amp;face=0_0_3840_2224&quot;&gt;&lt;a href=&quot;https://cmux.com/ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cmux.com/ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cyWVZz/dJMb8YpYbqv/OQriXnUGkO7NoHOkIuyyZ0/img.png?width=3840&amp;amp;height=2224&amp;amp;face=0_0_3840_2224');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;cmux &amp;mdash; 멀티태스킹을 위해 만든 터미널&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트를 위한 네이티브 macOS 터미널. Claude Code, Codex, OpenCode, Gemini CLI, Kiro, Aider 등 모든 CLI 도구와 호환.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cmux.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.aim-intelligence.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.aim-intelligence.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777215095808&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;AIM Intelligence | Enterprise AI Security Platform&quot; data-og-description=&quot;From automated red teaming to real-time guardrails &amp;mdash; AIM Intelligence delivers complete control over enterprise AI.&quot; data-og-host=&quot;aim-intelligence.vercel.app&quot; data-og-source-url=&quot;https://www.aim-intelligence.com/&quot; data-og-url=&quot;https://aim-intelligence.vercel.app&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpW0A5/dJMb9dHqY6x/RIV1R1g12MBoA3Lalklr4K/img.png?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160,https://scrap.kakaocdn.net/dn/coXbwT/dJMb9g5dWgP/vDvVHQz6jtDlMzi43vmSf1/img.png?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160&quot;&gt;&lt;a href=&quot;https://www.aim-intelligence.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.aim-intelligence.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpW0A5/dJMb9dHqY6x/RIV1R1g12MBoA3Lalklr4K/img.png?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160,https://scrap.kakaocdn.net/dn/coXbwT/dJMb9g5dWgP/vDvVHQz6jtDlMzi43vmSf1/img.png?width=3840&amp;amp;height=2160&amp;amp;face=0_0_3840_2160');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AIM Intelligence | Enterprise AI Security Platform&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;From automated red teaming to real-time guardrails &amp;mdash; AIM Intelligence delivers complete control over enterprise AI.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aim-intelligence.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;해커톤에서 느낀 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에 대해선 깊게 사용해 보지 않고 AI를 이용한 서비스를 사용만 해봐서 그쪽 분야에 대한 이해가 깊진 않았다. 그래도 AI를 이용한 개발의 전체 프로세스를 경험해 보고자 참여했고 나름대로 의의가 있었던 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x8w48/dJMcaaryN1A/8beNFE77zlP85LNNrAxJQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x8w48/dJMcaaryN1A/8beNFE77zlP85LNNrAxJQk/img.png&quot; data-alt=&quot;해커톤 현장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x8w48/dJMcaaryN1A/8beNFE77zlP85LNNrAxJQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx8w48%2FdJMcaaryN1A%2F8beNFE77zlP85LNNrAxJQk%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;540&quot; height=&quot;405&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해커톤 현장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;AI를 개발에 적극 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 당일은 내가 하루에 AI를 가장 많이 사용한 날인 것 같다. 기획부터 개발까지 모든 분야에 AI를 사용했다. 사용하다 보니 나름의 특색이 있지 않고 단순히 개발만 한다면 언젠가는 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;원래는 AI를 사용하는게 어색했는데 해커톤을 겪고 나니 조금은 친해진 것 같다. 앞으로 하고 싶은 프로젝트가 있다면 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;2️⃣ &lt;b&gt;인풋의 차이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 심사가 끝나고 수상작들 발표를 듣는데 AI를 사용하는 수준이 나와 차원이 달랐던 것 같다. 개발할 때 혹은 현업에서 일할 때 만나는 문제를 직접적으로 생각하고 자신이 가진 지식을 이용해서 솔루션을 제시하는 것이 깊이가 달랐다. 혼자서 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;제일 관심 있게 본 것은 현직 기업 보안 담당자 두분이 개발한 AI 패키지 스캐너였다. OS Scanner와 같이 빅테크가 개발한 오픈 소스 보안 스캐너가 있는데, CVE에 등재가 안되면 감지하기 어려운 단점이 있었다. 현업에서는 매번 인식 가능한 공격만 들어오면 아니기도 하고, 어떤 패키지가 감염됐는지 매 순간마다 파악하기 어렵기 때문에 이런 것을 극복하기 위한 AI 스캐너를 개발한 것이다.&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;주제를 듣고 솔루션에 대한 발표를 들으니 AI를 활용할 때 특정 수준을 넘기 위해선 그에 상응하는, 혹은 수많은 경험과 지식이 있어야 한다는 것을 다시 한번 느낀 것 같다. 나도 지식을 쌓고 AI를 더 자주 사용해 그런 특이점을 넘어보고 싶다.&lt;/p&gt;</description>
      <category>Experience</category>
      <category>해커톤 후기</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/59</guid>
      <comments>https://brotherjeantech.tistory.com/59#entry59comment</comments>
      <pubDate>Mon, 27 Apr 2026 00:11:34 +0900</pubDate>
    </item>
    <item>
      <title>[LangChain] LangChain을 이용한 LLM 실습</title>
      <link>https://brotherjeantech.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 LangChain이 무엇인지, 왜 필요한지, 어떻게 쓰일 수 있는지를 실습을 통해 학습한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LangChain, Model, Structured Output, Memory, LangSmith&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;a href=&quot;https://wikidocs.net/331266&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/331266&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776149941906&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;01장. 시작하며&quot; data-og-description=&quot;이 책에서는 랭체인과 랭그래프 1.0을 기반으로, 실무 현장에 바로 투입할 수 있는 **'AI 에이전트'**의 단계별 구현 방법을 다룹니다. 단순히 질문에 답하는 평범한 챗봇을 &amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/331266&quot; data-og-url=&quot;https://wikidocs.net/331266&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ZuAcL/dJMb86O28XL/V4H7oK2PwNTKtHaLimFYn1/img.jpg?width=930&amp;amp;height=1207&amp;amp;face=0_0_930_1207&quot;&gt;&lt;a href=&quot;https://wikidocs.net/331266&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/331266&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ZuAcL/dJMb86O28XL/V4H7oK2PwNTKtHaLimFYn1/img.jpg?width=930&amp;amp;height=1207&amp;amp;face=0_0_930_1207');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;01장. 시작하며&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 책에서는 랭체인과 랭그래프 1.0을 기반으로, 실무 현장에 바로 투입할 수 있는 **'AI 에이전트'**의 단계별 구현 방법을 다룹니다. 단순히 질문에 답하는 평범한 챗봇을 &amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;LangChain이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랭체인은 LLM을 이용할 때 발생하는 파편화된 작업들을 통일된 규격으로 묶어 통해 구현할 수 있게 해주는 프레임워크다. 단순한 LLM은 사용자의 질문에 텍스트 형태로 답을 한다. LangChain은 LLM을 사용해 애플리케이션을 개발하거나 에이전트를 개발할 때 도움을 주는 프레임워크다.&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;LangChain을 사용하면 고도화된 AI Agent를 만들 수 있다. Agent의 세부 동작을 설정할 수 있기에 Model, Structured Ouput, Memory등 Agent의 성능에 영향을 주는 메뉴들을 직접 조작할 수 있다. 특히 가드레일과 같은 보안을 위한 미들웨어 개발에도 큰 도움을 준다.&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;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;이처럼 LangChain은 LLM을 이용한 서비스를 개발하는데 도움을 준다. 그러나, LangChain을 사용할 경우 비용과 시간, 보안 등 관리해야 할 항목이 늘어나기 때문에 복잡성이 증가한다. 성공적인 LLM 서비스 설계를 위해선 흑백논리가 아닌,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;'어디까지 파이프라인으로 통제하고, 어디부터 에이전트에게 자율성을 부여할 것인가'&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;를 결정해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Model&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain은 &lt;b&gt;개발자가 설정한 모델&lt;/b&gt;을 기준으로 동작한다. 사실 LLM에 틀을 입혀 개발자가 관리하기 편하게 제공하는게 LangChain이기 때문에 개발자는 환경변수 등을 이용해 자신이 사용할 LLM API Key를 입력해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfvoNV/dJMcah48Piv/JosPKIAVg7zmG713GhQ1SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfvoNV/dJMcah48Piv/JosPKIAVg7zmG713GhQ1SK/img.png&quot; data-alt=&quot;구글 코랩을 이용한 LangChain 실습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfvoNV/dJMcah48Piv/JosPKIAVg7zmG713GhQ1SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfvoNV%2FdJMcah48Piv%2FJosPKIAVg7zmG713GhQ1SK%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;540&quot; height=&quot;335&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;구글 코랩을 이용한 LangChain 실습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 설정하면 개발자는 invoke() 메소드를 사용해 LLM을 호출할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain에 내장되어 있는 create_model() 메소드를 통해 모델에 대한 시스템 프롬프트, 툴을 지정할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  &lt;b&gt;모델 호출 결과&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;{'input_tokens': 15, 'output_tokens': 824, 'total_tokens': 839, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 640}}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론 결과를 보면 'reasoning'이라는 항목이 보인다. LLM은 개발자의 답변을 생성하기 위해 '추론'이라는 과정을 거치는데, 답변의 2/3을 차지할 정도로 추론의 양이 많다. 추론의 양을 많게 설정하면 LLM은 창의적이고 다양한 말 표현을 생각해 내며 문제를 해결하기 위해 답변을 깊게 생각한다. 반대로 추론의 양이 적다면 문제 해결을 위해 깊게 생각하기 보단 사실을 위주로 생각할 확률이 높아진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Structured Output&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI Agent를 이용하기 위해선 각 도구들이나 다른 LLM들에게 다른 내용을 주어선 안된다. 평소 LLM처럼 추상적이고 언어로 이루어진 말을 전달할 경우 통일된 동작을 이끌어낼 수 없고, LLM들마다 다른 결과를 가져오기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 엔지니어링의 DTO처럼 &lt;b&gt;구조화된 답변&lt;/b&gt;을 사용해야 컴포넌트들에게 통일된 데이터를 전달할 수 있고 에러가 발생할 경우 원인을 찾기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조화된 답변은 흔히 &lt;b&gt;데이터 스키마&lt;/b&gt;라고 한다. &lt;b&gt;데이터 스키마&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;란 데이터의 구조, 타입(문자열, 숫자, 리스트 등), 그리고 제약 조건을 명시해 둔 일종의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;'설계도'&lt;/b&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;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;모델에게 명확한 스키마를 제공하는 것은, 모델과 시스템 사이에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&quot;반드시 이 필드명과 데이터 타입을 준수하고, 필수 여부 규칙에 맞춰 데이터를 생성하라&quot;는 엄격한 계약&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;을 맺는 것과 같다. 설계도가 정교할수록 모델이 엉뚱한 답변을 내놓는 환각(Hallucination) 현상이 현저히 줄어들며, 전체 시스템의 안정성은 비약적으로 상승한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;데이터 스키마대로 구조화된 데이터를 만들기 위해선 '구조화 전용 래퍼'가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain에선 구조화된 답변을 구성하기 위해 두 가지 방법을 사용한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Pydantic (Python 환경)&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;Pydantic&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpMf8D/dJMcahcZTtz/NhNGVT9EBG4Y5YScEMzM4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpMf8D/dJMcahcZTtz/NhNGVT9EBG4Y5YScEMzM4k/img.png&quot; data-alt=&quot;Colab으로 구성한 Pydantic 코드 및 AI 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpMf8D/dJMcahcZTtz/NhNGVT9EBG4Y5YScEMzM4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpMf8D%2FdJMcahcZTtz%2FNhNGVT9EBG4Y5YScEMzM4k%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;540&quot; height=&quot;295&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Colab으로 구성한 Pydantic 코드 및 AI 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 환경에서는 Pydantic을 이용해 데이터 스키마를 정의할 수 있다. Pydantic은 Python 환경에서 사용하는 데이터 래퍼다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;JSON&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCBKau/dJMcacW3bJM/9e1TcCoOlMqfYKTWak0TT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCBKau/dJMcacW3bJM/9e1TcCoOlMqfYKTWak0TT0/img.png&quot; data-alt=&quot;JSON 데이터 스키마&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCBKau/dJMcacW3bJM/9e1TcCoOlMqfYKTWak0TT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCBKau%2FdJMcacW3bJM%2F9e1TcCoOlMqfYKTWak0TT0%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;540&quot; height=&quot;626&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JSON 데이터 스키마&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBwVJA/dJMcabw4rfY/CaahiWPKcUTEwm3YOkQaL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBwVJA/dJMcabw4rfY/CaahiWPKcUTEwm3YOkQaL0/img.png&quot; data-alt=&quot;JSON 데이터 스키마를 활용한 AI 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBwVJA/dJMcabw4rfY/CaahiWPKcUTEwm3YOkQaL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBwVJA%2FdJMcabw4rfY%2FCaahiWPKcUTEwm3YOkQaL0%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;540&quot; height=&quot;190&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JSON 데이터 스키마를 활용한 AI 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서비스를 개발할 때 흔히 사용하는 JSON을 가지고도 구조화된 답변을 설계할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 서비스가 웹 플랫폼과 합쳐져 사용된다면 JSON을 사용하는게 확장성을 높일 수 있다. Python 내에선 Dict 자료구조로 사용된다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Memory&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 기본적으로 'stateless'다. 사용자가 보낸 말의 맥락을 기억하지 않는다. LLM으로 하여금 사용자의 말을 기억하게 하고 싶다면, LangChain의 자체 기능이나 데이터베이스처럼 사용자의 말을 기억할 수단이 필요하다(그리고 그 말을 LLM에게 전달하는 로직도 필요하다).&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;전에 Spring AI를 활용해 LLM용 데이터베이스 인터페이스를 만든 적이 있는데, LangChain에서는 이를 한 기능으로 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain은 데이터베이스에 저장할 말을 분류하기 위해 '메세지 종류'를 활용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;LangChain 메세지 종류&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;System Message (시스템 메시지)&lt;/b&gt;: 개발자가 미리 부여하는 &quot;역할 및 규칙 프롬프트&quot;, 모델의 답변 톤과 관점이 해당 역할에 맞춰진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Human/User Message (사용자 메시지)&lt;/b&gt;: 사용자가 입력하는 질문이나 지시사항&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI/Assistant Message (AI 메시지)&lt;/b&gt;: 모델이 생성한 답변&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 메세지들을 토대로 다음 답변을 설계한다. 메세지들을 위의 분류대로 저장하고 있다가 다음 메세지가 보내지면 태그와 함게 LLM에게 전송한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;LangChain &lt;/b&gt;&lt;b&gt;메세지를 보내는 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메세지 객체를 활용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5sLHj/dJMcaadTtPg/Nlsqpp1lQU3KIi90fFMOy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5sLHj/dJMcaadTtPg/Nlsqpp1lQU3KIi90fFMOy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5sLHj/dJMcaadTtPg/Nlsqpp1lQU3KIi90fFMOy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5sLHj%2FdJMcaadTtPg%2FNlsqpp1lQU3KIi90fFMOy1%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;540&quot; height=&quot;219&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dict 객체 활용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Wx1i/dJMcagrCFqE/ra8xFCOlSfr08I3IEyVkMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Wx1i/dJMcagrCFqE/ra8xFCOlSfr08I3IEyVkMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Wx1i/dJMcagrCFqE/ra8xFCOlSfr08I3IEyVkMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Wx1i%2FdJMcagrCFqE%2Fra8xFCOlSfr08I3IEyVkMk%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;540&quot; height=&quot;294&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;Message에 대한 생각&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 대화를 기억하고 답변을 설계하면 문맥에 맞는 답변을 받을 수 있어 편리하다. 다만, LLM과의 대화를 기억하고 이를 매번 전달하는 것은 시스템 자원을 사용하는 일이므로 편리함과 자원 효율성 사이에서 고민해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 LLM에 메모리를 도입할 때 고려할 수 있는 방안이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;슬라이딩 윈도우 : 최근 텍스트로부터 N개만 기억하기&lt;/li&gt;
&lt;li&gt;토큰 기반 예산 트리밍 : 정해 놓은 토큰을 넘어선다면 가장 나중 메세지 자르기&lt;/li&gt;
&lt;li&gt;전체 대화 요약 + 최근 대화 저장 : 전체 대화는 요약해서 전달하고, 최근 대화만 저장 후 전달&lt;/li&gt;
&lt;li&gt;상태 사용 : 모든 메세지에 공통으로 저장돼야 하는 사용자 정보, 시스템 프롬프트 등은 '상태'로 저장&lt;/li&gt;
&lt;li&gt;검색 기반 메모리 RAG 사용&lt;/li&gt;
&lt;/ol&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 id=&quot;code_1776238328739&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.messages.utils import trim_messages, count_tokens_approximately

trimmed_messages = trim_messages(
    messages,
    strategy=&quot;last&quot;, # 오래된 대화를 버리고 최근 대화를 유지
    token_counter=count_tokens_approximately,
    max_tokens=2000,
    include_system=True, # 시스템 메시지는 잘려나가지 않도록 고정(핀)
    start_on=&quot;human&quot;, # 잘라냈을 때 첫 시작이 무조건 사람의 질문이 되도록 보장
    end_on=(&quot;human&quot;, &quot;tool&quot;),
)

response = model.invoke(trimmed_messages)&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;LangSmith&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangSmith는 LangChain을 사용해 LLM과 대화할 때 일어나는 모든 작업에 대한 정보를 모니터링 하는 툴이다. 이를 통해 대화 내역, 토큰 사용 수, 애플리케이션 작업을 추적할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WeIIo/dJMcabw4uiK/KzKmIteH5NcvDSGK5kwP8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WeIIo/dJMcabw4uiK/KzKmIteH5NcvDSGK5kwP8k/img.png&quot; data-alt=&quot;실제 LangSmith 화면, 대화 내역이 나와있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WeIIo/dJMcabw4uiK/KzKmIteH5NcvDSGK5kwP8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWeIIo%2FdJMcabw4uiK%2FKzKmIteH5NcvDSGK5kwP8k%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;600&quot; height=&quot;319&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제 LangSmith 화면, 대화 내역이 나와있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangSmith를 활용하면 언제, 어떤 입력이 일어났는지 쉽게 알 수 있기 때문에 AI Agent 서비스를 쉽게 모니터링할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZyFQT/dJMcaaEYyq1/c0fMiACE5vd6c2a4sUOxz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZyFQT/dJMcaaEYyq1/c0fMiACE5vd6c2a4sUOxz0/img.png&quot; data-alt=&quot;빈 공간에 랭스미스 키를 입력하고, 추적 기능을 키면 해당 세션부터 LLM 대화 내역을 감지한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZyFQT/dJMcaaEYyq1/c0fMiACE5vd6c2a4sUOxz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZyFQT%2FdJMcaaEYyq1%2Fc0fMiACE5vd6c2a4sUOxz0%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;540&quot; height=&quot;357&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈 공간에 랭스미스 키를 입력하고, 추적 기능을 키면 해당 세션부터 LLM 대화 내역을 감지한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangSmith의 기본 내용을 사용해봤다. 확실히 LLM API를 단독으로 사용해 어플리케이션을 기록할 때보다 훨씬 편해보인다. 이를 이용해서 개인 서버에서 AI Agent를 한번 구축해보고 싶다. 그리고 관련된 보안을 어떻게 관리할지 궁금하다. 이것도 찾아봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[출처]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.samsungsds.com/kr/insights/what-is-langchain.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.samsungsds.com/kr/insights/what-is-langchain.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776238874341&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;랭체인 LangChain 이란 무엇인가? | 인사이트리포트 | 삼성SDS&quot; data-og-description=&quot;이 글에서는 AI 시대의 변화와 함께 LangChain이 어떻게 언어 모델의 한계를 극복하고 실질적인 애플리케이션 개발을 가능하게 하는지 살펴보겠습니다.&quot; data-og-host=&quot;www.samsungsds.com&quot; data-og-source-url=&quot;https://www.samsungsds.com/kr/insights/what-is-langchain.html&quot; data-og-url=&quot;https://www.samsungsds.com/kr/insights/what-is-langchain.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b9uCZq/dJMb9b3Txxh/yqGmo9ozYbtEXNkKH3uyo0/img.png?width=1860&amp;amp;height=1044&amp;amp;face=0_0_1860_1044,https://scrap.kakaocdn.net/dn/bI9MeW/dJMb9eTQ9VJ/yNxWAwFbgSok93LJ7hpMSk/img.png?width=1860&amp;amp;height=1044&amp;amp;face=0_0_1860_1044,https://scrap.kakaocdn.net/dn/bltxqe/dJMb86O3fCK/NK4exgA5kUprqgRKZrNK71/img.jpg?width=1207&amp;amp;height=787&amp;amp;face=0_0_1207_787&quot;&gt;&lt;a href=&quot;https://www.samsungsds.com/kr/insights/what-is-langchain.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.samsungsds.com/kr/insights/what-is-langchain.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b9uCZq/dJMb9b3Txxh/yqGmo9ozYbtEXNkKH3uyo0/img.png?width=1860&amp;amp;height=1044&amp;amp;face=0_0_1860_1044,https://scrap.kakaocdn.net/dn/bI9MeW/dJMb9eTQ9VJ/yNxWAwFbgSok93LJ7hpMSk/img.png?width=1860&amp;amp;height=1044&amp;amp;face=0_0_1860_1044,https://scrap.kakaocdn.net/dn/bltxqe/dJMb86O3fCK/NK4exgA5kUprqgRKZrNK71/img.jpg?width=1207&amp;amp;height=787&amp;amp;face=0_0_1207_787');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;랭체인 LangChain 이란 무엇인가? | 인사이트리포트 | 삼성SDS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 AI 시대의 변화와 함께 LangChain이 어떻게 언어 모델의 한계를 극복하고 실질적인 애플리케이션 개발을 가능하게 하는지 살펴보겠습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.samsungsds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>AI</category>
      <category>LangChain</category>
      <category>langchain memory</category>
      <category>langsmith</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/58</guid>
      <comments>https://brotherjeantech.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 15 Apr 2026 16:42:36 +0900</pubDate>
    </item>
    <item>
      <title>[Nginx] Directives, Context 그리고 Configuration</title>
      <link>https://brotherjeantech.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 'nginx.conf' 설정 파일에서 Nginx 서버에 관한 다양한 설정을 정할 수 있다. 파일 안에는 Directives와 Context이 모여 Configuration을 만든다. 계층 순서대로 Directives부터 Configuration까지 알아보도록 하자.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Directives&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Directives는 config 파일 안의 지시어다. 파일의 가장 기본 단위이자 서버가 실행하는 명령어라 생각하면 된다. Directives는 크게 두 가지로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Single Directives&lt;/li&gt;
&lt;li&gt;Block Directives&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 뜻대로 Directives는 한 줄로 구성할 수 있고, 중괄호를 사용해 섹션 혹은 블록처럼 구성할 수 있다. 같은 내용의 설정이 많이 필요한 경우 Directives를 블록으로 구성해야할 필요가 있을 것이다. 예시를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1776065071347&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user             nobody;
error_log        logs/error.log notice;
worker_processes 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 설정들은 Single Directives를 이용해 구성한 설정이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776065365182&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user nobody; # a directive in the 'main' context

events {
    # configuration of connection processing
}

http {
    # Configuration specific to HTTP and affecting all virtual servers

    server {
        # configuration of HTTP virtual server 1
        location /one {
            # configuration for processing URIs starting with '/one'
        }
        location /two {
            # configuration for processing URIs starting with '/two'
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event, http, server, location 모두 Block Directives로 구성한 것이다. 각각의 블록들은 설정의 문맥인 Context를 설정한다. Block Directives는 단순히 명령어라기 보다는 규칙 집합을 만드는 것과 같다. 따라서 자연스럽게 Context를 생성하는 것이다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Context&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'nginx.conf' 파일 내에서 지시어가 효력을 발휘하는 '영역'이다. 프로그래밍 언어의 Scope(변수 범위)와 아주 비슷하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http 블록 안에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Directive를 적으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;해당 Directive는 웹 서비스 전체에 적용된다.&lt;/li&gt;
&lt;li&gt;server 블록 안에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Directive를 적으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;해당 Directive는 특정 도메인에만 적용된다.&lt;/li&gt;
&lt;li&gt;location 블록 안에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Directive를 적으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;해당 Directive는 특정 URL 경로에만 적용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 글이 모여 문단을 만들고 그 속에 문맥이 있듯, 여러 개의 Directive가 모여 설정에 대한 '문맥'을 만든다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Context에는 계층이 있다. 위의 예시를 다시 가져오자.&lt;/p&gt;
&lt;pre id=&quot;code_1776065675002&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user nobody; # a directive in the 'main' context

events {
    # configuration of connection processing
}

http {
    # Configuration specific to HTTP and affecting all virtual servers

    server {
        # configuration of HTTP virtual server 1
        location /one {
            # configuration for processing URIs starting with '/one'
        }
        location /two {
            # configuration for processing URIs starting with '/two'
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http 블록 안에는 server와 location이 있고, server안에 location이 있다. 각각의 Context는 계층을 가지고 있으며, 상위 계층에서 적용한 설정은 하위 계층에 전부 적용 된다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Configurations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Configuration은 Nginx 서버 설정 전체이자 상태를 의미한다. Nginx 서버는 'nginx.conf' 파일을 통해 설정을 명시하는데, Configuration은 파일 전체 혹은 서버 설정값 전체를 의미한다.&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;예를 들어 nginx 파일에서 Content Caching 기능을 사용하기 위해 디스크와 메모리를 설정했다면 이 상태가 해당 Nginx 서버의 Configuration이 된다. (그냥 nginx.conf 파일값이라 보면 된다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[출처]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776065732773&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Create NGINX Plus and NGINX Configuration Files | NGINX Documentation&quot; data-og-description=&quot;Create NGINX Plus and NGINX Configuration Files NGINX and NGINX Plus use a text‑based configuration file, by default named nginx.conf. NGINX Plus: default location is /etc/nginx for Linux or /usr/local/etc/nginx for FreeBSD. NGINX Open Source: location d&quot; data-og-host=&quot;docs.nginx.com&quot; data-og-source-url=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot; data-og-url=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ZVfaa/dJMb9jgyypu/ZLkFBkrVh914LII67Kyl0k/img.png?width=500&amp;amp;height=300&amp;amp;face=0_0_500_300&quot;&gt;&lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ZVfaa/dJMb9jgyypu/ZLkFBkrVh914LII67Kyl0k/img.png?width=500&amp;amp;height=300&amp;amp;face=0_0_500_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Create NGINX Plus and NGINX Configuration Files | NGINX Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Create NGINX Plus and NGINX Configuration Files NGINX and NGINX Plus use a text‑based configuration file, by default named nginx.conf. NGINX Plus: default location is /etc/nginx for Linux or /usr/local/etc/nginx for FreeBSD. NGINX Open Source: location d&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.nginx.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps</category>
      <category>Configuration</category>
      <category>CONTEXT</category>
      <category>Directive</category>
      <category>nginx</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/57</guid>
      <comments>https://brotherjeantech.tistory.com/57#entry57comment</comments>
      <pubDate>Mon, 13 Apr 2026 16:36:46 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] Labels &amp;amp; Selectors</title>
      <link>https://brotherjeantech.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스에서 리소스(쿠버네티스 내에선 Object이라 불린다)를 구별하는 Label과 Selector를 알아보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Label &amp;amp; Selectors&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Labels &amp;amp; Selectors&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨과 셀렉터라는 개념은 처음 들어본다. 아마 쿠버네티스에서 리소스를 구별할 때 쓰는 것 같은데, 공식문서를 보고 의미를 찾아보자.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;라벨이란?&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot; style=&quot;background-color: #f8f8f8; color: #222222; text-align: left;&quot;&gt;&lt;code&gt;&quot;metadata&quot;: {
  &quot;labels&quot;: {
    &quot;key1&quot; : &quot;value1&quot;,
    &quot;key2&quot; : &quot;value2&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨은 리소스에 붙이는 &lt;b&gt;키-값 쌍 데이터&lt;/b&gt;이다. 파드와 같이 쿠버네티스에서 사용하는 리소스들에 붙이는 이름표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨은 파드를 처음 생성할 때 붙일 수도 있고, 그 후에도 사용자가 원할 때 언제든지 붙일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 리소스 당 하나의 라벨을 붙일 수도 있고, 한 집합에 안에 속하는 리소스들 사이에서 부분 집합만 선택하도록 라벨을 설정할 수 있다. (한 노드에 있는 여러 파드들 중 몇 개만 라벨로 묶을 수 있는 소리 같다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1775546365590&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;metadata&quot;: {
  &quot;labels&quot;: {
    &quot;key1&quot; : &quot;value1&quot;,
    &quot;key2&quot; : &quot;value2&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose를 통해 컨테이너를 생성할 때 docker-compose.yml 파일에서 컨테이너의 이름을 생성할 수 있는데, 그것이랑 똑같은 개념인 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &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;예를 들어, 내가 노드 A와 노드 B를 설정했다고 하자. 각각의 노드는 1번부터 10번까지의 파드를 가지고 있다. 만약 사용자가 노드 A의 1번부터 5번까지, 그리고 노드 B의 1번부터 5번까지의 파드를 묶어 프론트엔드 서버로 관리하고 싶을 수 있다. 관리하던 도중 파드 안에 있는 서버들에 수정사항이 생긴다면, 사용자는 이 파드들을 하나씩 접속하면서 변경 사항을 적용해야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨은 바로 이때 필요하다. 클러스터를 통해 서비스를 배포하면 각 노드들은 수많은 '상태(혹은 차원)'을 가지게 된다. 클러스터 전체로 놓고 보면 이러한 차원들이 굉장히 많을 것이다. 문제는 이런 상황에서 각 노드들을 관리해야 하는 경우인데, 각 노드들이 다양한 집합에 소속되거나 상태가 다양하다면 관리자가 이를 관리하기 매우 힘들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨은 이러한 Cross-Cutting Operation(&lt;span style=&quot;background-color: #ffffff; color: #0a0a0a; text-align: start;&quot;&gt;어플리케이션의 여러 모듈이나 계층에 걸쳐서 공통적으로 적용되어야 하는 기능이나 동작) 상황에서 기존 구조를 깨뜨리지 않은 채 각 리소스를 관리할 수 있게끔 도와준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;네임스페이스와 차이점은 무엇인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네임스페이스는 리소스 사이에 설정하는 경계이고, 라벨은 리소스들에 붙이는 이름표이다. 리소스들은 경계를 넘어갈 수 없는 대신 여러 개의 이름표를 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊게 설명하자면 리소스는 자신이 유일하게 결정되는 물리적인 경계이다. 한 네임스페이스 안에서는 동일한 이름을 사용하지 못한다. 반면 라벨은 네임스페이스에 상관없이 붙일 수 있는 속성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무적인 상황에서 예를 들어보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mczHe/dJMcajaHmdU/xQntDa1OYv90plwdREhIkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mczHe/dJMcajaHmdU/xQntDa1OYv90plwdREhIkk/img.png&quot; data-alt=&quot;노드 2개로 구성되어 있는 노드 클러스터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mczHe/dJMcajaHmdU/xQntDa1OYv90plwdREhIkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmczHe%2FdJMcajaHmdU%2FxQntDa1OYv90plwdREhIkk%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;520&quot; height=&quot;334&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노드 2개로 구성되어 있는 노드 클러스터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네임스페이스는 '경계'이다. Namespace A에서 A-01은 유일하다. A-01은 Namespace A에서 유일하게 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 라벨은 속성이다. Namespace A에서 Frontend와 Namespace B에서의 Frontend는 같다. 라벨은 리소스 사이의 다양한 계층과 물리적 거리를 뛰어넘어 적용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;라벨 셀렉터는 무엇인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀렉터는 쿠버네티스의 리소스를 선택하는 필드이자 필터이다. 파드나 컨테이너처럼 리소스 자체는 아니고, 리소스 안에 포함되어 있는 검색 엔진 정도로 생각하면 될듯하다. 쿠버네티스는 두 종류의 셀렉터를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Equality-Based Selector&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일치/불일치 조건을 통해 원하는 리소스를 필터링 하는 방법이다. =, ==('=' 와 같음), != 세 개의 연산자가 허용된다. 연산자들을 조합해 특정 조건에 맞는 리소스를 찾을 수 있다. 밑에 예시를 보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1775630466558&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;environment = production
tier != frontend&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 라벨 필터를 적용해 원하는 리소스를 찾는 과정이다. 이미 배포된 라벨 리소스 중 프론트엔드가 아닌 리소스만 선정하는 과정이다. 이런식으로 일치/불일치 조건을 통해 라벨로 원하는 리소스를 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;Set-Based Selector&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합 조건을 통해 원하는 리소스를 필터링 하는 방법이다. in, notin, exists를 통해 원하는 리소스를 찾는다. 예시를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1775630872964&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;environment in (production, qa)
tier notin (frontend, backend)
partition
!partition&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 예시는 environment라는 키에서 production, qa 라는 value를 가진 리소스를 찾는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 tier 키에서 frontend, backend라는 value를 가진 리소스를 제외한 나머지를 찾는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째는 오직 partition이라는 key를 가진 전체 리소스를 찾는 것이고, 네 번째는 제외해 찾는다.&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;참고로 , 는 AND 연산자 처럼 동작하며, Set-Based는 Equality-Based와 혼합해서 사용할 수 있다.&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;4️⃣ &lt;b&gt;라벨을 효율적으로 쓰는 방법은?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 라벨을 효과적으로 쓰는 방법은 무엇일까? 하나의 리소스에 하나의 라벨을 붙일 수 있지만, 이는 권장되는 방법이 아니다. 애초에 라벨은 여러 리소스에 붙여 사용하도록 설계됐기 때문이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 서비스에서 여러 tier의 리소스들이 동시에 동작하고 있고, 각각의 관리가 필요할 때 라벨의 사용을 고려할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드와 Redis를 사용한 여러 리소스들이 존재하는 서비스일 때, 어떻게 라벨을 관리하는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1775632126603&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: frontend&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;pre id=&quot;code_1775632149683&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: backend
  role: master&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Redis의 마스터 리소스라면 이렇게 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1775632175844&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: backend
  role: replica&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Replica면 이렇게 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[출처]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775632194147&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Labels and Selectors&quot; data-og-description=&quot;Labels are key/value pairs that are attached to objects such as Pods. Labels are intended to be used to specify identifying attributes of objects that are meaningful and relevant to users, but do not directly imply semantics to the core system. Labels can &quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&quot; data-og-url=&quot;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/l6h4D/dJMb9frGjMG/qiM5DVhz5nRSmjmklOowP0/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/l6h4D/dJMb9frGjMG/qiM5DVhz5nRSmjmklOowP0/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Labels and Selectors&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Labels are key/value pairs that are attached to objects such as Pods. Labels are intended to be used to specify identifying attributes of objects that are meaningful and relevant to users, but do not directly imply semantics to the core system. Labels can&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서를 통해 라벨과 셀렉터를 봤는데, 나중에 기술 블로그를 찾아봐서 실제로 어떻게 쓰이는지 공부해야겠다.&lt;/p&gt;</description>
      <category>DevOps/Kubernetes</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/56</guid>
      <comments>https://brotherjeantech.tistory.com/56#entry56comment</comments>
      <pubDate>Wed, 8 Apr 2026 16:19:51 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] 쿠버네티스 네트워크 개요(CNI, Pod-to-Pod, Service, Ingress)</title>
      <link>https://brotherjeantech.tistory.com/55</link>
      <description>&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;CNI, Pod-to-Pod, Service, Ingress&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;CNI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CNI는 &lt;b&gt;컨테이너 네트워크 인터페이스&lt;/b&gt;의 약자로, 쿠버네티스 안에서 컨테이너들의 네트워크를 설정하기 위한 인터페이스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 Pod 안의 컨테이너들의 네트워크를 '직접' 설정하지 않는다. Pod는 각자 IP와 볼륨을 필요로 한다는 규칙만 언급할 뿐 구현은 외부 플러그인에 맡기고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 네트워크 플러그인이 존재하며, 서버와 클러스터의 상황에 따라 적절한 외부 플러그인을 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  &lt;b&gt;쿠버네티스 네트워크 모델&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;클러스터의 모든 파드는 고유한 IP 주소를 갖는다. 이는 즉 &lt;u&gt;파드간 연결을 명시적으로 만들 필요가 없으며 또한 컨테이너 포트를 호스트 포트에 매핑할 필요가 거의 없음&lt;/u&gt;을 의미한다. 이로 인해 포트 할당, 네이밍, 서비스 디스커버리, 로드 밸런싱, 애플리케이션 구성, 마이그레이션 관점에서 &lt;u&gt;파드를 VM 또는 물리 호스트처럼 다룰 수 있는 깔끔하고 하위 호환성을 갖는 모델&lt;/u&gt;이 제시된다.&lt;/blockquote&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;span style=&quot;letter-spacing: 0px;&quot;&gt;쿠버네티스는 모든 네트워킹 구현에 대해 다음과 같은 근본적인 요구사항을 만족할 것을 요구한다. (이를 통해, 의도적인 네트워크 분할 정책을 방지)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파드는 NAT 없이 노드 상의 모든 파드와 통신할 수 있다.&lt;/li&gt;
&lt;li&gt;노드 상의 에이전트(예: 시스템 데몬, kubelet)는 해당 노드의 모든 파드와 통신할 수 있다.&lt;/li&gt;
&lt;li&gt;파드 내의 컨테이너는 루프백(loopback)을 통한&lt;span&gt;&amp;nbsp;&lt;/span&gt;네트워킹을 사용하여 통신한다.&lt;/li&gt;
&lt;li&gt;클러스터 네트워킹은 서로 다른 파드 간의 통신을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서비스&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;API를 사용하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;파드에서 실행 중인 애플리케이션을 클러스터 외부에서 접근&lt;span&gt;&amp;nbsp;&lt;/span&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인그레스&lt;/b&gt;는 특히 HTTP 애플리케이션, 웹사이트 그리고 API를 노출하기 위한 추가 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;또한 서비스를 사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스를 클러스터 내부에서만 사용할 수 있도록 게시할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 요구사항이 있지만 결국 이것은 외부 플러그인으로 하여금 쿠버네티스가 구현해주길 원하는 요구사항이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 건 쿠버네티스는 네트워크에 대한 규칙만 줄 뿐, 세부적인 구현사항은 외부 플러그인마다 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스가 제공하는 모든 클러스터와 노드들은 구축돼있는 네트워크 리소스들을 이용해 통신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[출처]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%AA%A8%EB%8D%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.io/ko/docs/concepts/services-networking/#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%AA%A8%EB%8D%B8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774859351175&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;서비스, 로드밸런싱, 네트워킹&quot; data-og-description=&quot;쿠버네티스의 네트워킹에 대한 개념과 리소스에 대해 설명한다.&quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%AA%A8%EB%8D%B8&quot; data-og-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/R82q0/dJMb9jgxikc/OIZmk3HiKioMoyZ2h8klAK/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%AA%A8%EB%8D%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/#%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%AA%A8%EB%8D%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/R82q0/dJMb9jgxikc/OIZmk3HiKioMoyZ2h8klAK/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;서비스, 로드밸런싱, 네트워킹&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스의 네트워킹에 대한 개념과 리소스에 대해 설명한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Pod-to-Pod Communication&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 노드안의 Pod들은 서로 어떻게 통신하는 것일까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 Pod들이 서로 통신할 수 있도록 가상의 리소스를 할당한다. 리소스를 할당할 때 가상의 스위치와 서브넷, 프라이빗 네트워크를 설정한다. 자세히 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ5qKd/dJMcahw7CT5/uUzmMJ3hr8ZINLdYLaunW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ5qKd/dJMcahw7CT5/uUzmMJ3hr8ZINLdYLaunW0/img.png&quot; data-alt=&quot;쿠버네티스 Pod 통신 도식 (출처 : https://coffeewhale.com/k8s/network/2019/04/19/k8s-network-01/)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ5qKd/dJMcahw7CT5/uUzmMJ3hr8ZINLdYLaunW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ5qKd%2FdJMcahw7CT5%2FuUzmMJ3hr8ZINLdYLaunW0%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;560&quot; height=&quot;447&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿠버네티스 Pod 통신 도식 (출처 : https://coffeewhale.com/k8s/network/2019/04/19/k8s-network-01/)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt; &lt;/span&gt; 기본 통신 구조 - IP&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Pod가 서로 통신하려면 외부 플러그인을 통해 발급받은 &lt;b&gt;IP&lt;/b&gt;가 필요하다. (같은 Pod 안의 컨테이너들은 같은 IP를 사용한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 IP를 사용하기 때문에 포트를 컨테이너 하나와 매핑시킬 수 있지만, 이건 쿠버네티스에선 추천하지 않는 방법이다.&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;API를 만들어 각 API 당 컨테이너 하나씩 할당하는게 쿠버네티스에서 권장하는 네트워크 설정 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 바로 &lt;b data-index-in-node=&quot;35&quot; data-path-to-node=&quot;11&quot;&gt;Service&lt;/b&gt;와 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;11&quot;&gt;Ingress&lt;/b&gt;라는 추상화 레이어를 사용해 DNS 수준에서 통신하는 방식이다. (나중에 설명한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;  &lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기본 네트워크 구조&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;963&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rOenx/dJMcaaY3AuB/0w9jV785supxsYfNCaoSkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rOenx/dJMcaaY3AuB/0w9jV785supxsYfNCaoSkk/img.png&quot; data-alt=&quot;쿠버네티스 기본 네트워크 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rOenx/dJMcaaY3AuB/0w9jV785supxsYfNCaoSkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrOenx%2FdJMcaaY3AuB%2F0w9jV785supxsYfNCaoSkk%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;560&quot; height=&quot;385&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;963&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿠버네티스 기본 네트워크 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 실질적인 네트워크 장치를 가지고 있지 않다. 네트워크에 필요한 요소들을 리소스로 구현한다.&lt;/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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 컴퓨터가 서로 통신하기 위해 랜선을 스위치에 연결하는 것을 쿠버네티스는 위의 리소스로 추상화한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 리소스가 어떤 역할을 하는지 간단하게 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9vzqG/dJMcajn57Eg/vDjNK7ovYPpkABeZB2OyqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9vzqG/dJMcajn57Eg/vDjNK7ovYPpkABeZB2OyqK/img.png&quot; data-alt=&quot;가상 이더넷과 브릿지는 홈 네트워크의 기기와 공유기를 생각하면 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9vzqG/dJMcajn57Eg/vDjNK7ovYPpkABeZB2OyqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9vzqG%2FdJMcajn57Eg%2FvDjNK7ovYPpkABeZB2OyqK%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;560&quot; height=&quot;192&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가상 이더넷과 브릿지는 홈 네트워크의 기기와 공유기를 생각하면 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &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;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;브릿지&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브릿지는 일종의 스위치다. 우리가 스위치에 여러 랜선을 꽂아서 서로 다른 컴퓨터들의 통신을 연결하듯이, 파드들이 가지고 있는 가상 이더넷을 브릿지에 꽂아 노드 내 파드들의 통신 관장하고, 노드 외부에서 온 통신을 적절한 파드로 연결하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;이더넷&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 개념의 네트워크 인터페이스다. 외부에서 노드를 확인하게끔 해주는 네트워크 식별자이다.&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;네트워크로 비유하면 노드가 가지고 있는 IP 주소, 네트워크 인터페이스이다.&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;4️⃣ &lt;b&gt;라우터&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 네트워크의 L4 스위치와 같다. 외부에서 온 패킷의 목적지를 확인한 후 적절한 노드로 보내주는 역할을 한다.&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;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;  &lt;b&gt;전송 과정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송과정을 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;노드 밖에서 패킷이 전송될 때 : 라우터 -&amp;gt; 이더넷(노드) -&amp;gt; 브릿지(노드 내부 스위치) -&amp;gt; 가상 이더넷(파드)&lt;/li&gt;
&lt;li&gt;노드 안에서 패킷이 전송될 때(노드 내부 통신) : 가상 이더넷(파드) -&amp;gt; 브릿지(노드 내부 스위치) -&amp;gt; 가상 이더넷(파드)&lt;/li&gt;
&lt;/ol&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Service &amp;amp; Ingress&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service와 Ingress는 쿠버네티스 네트워크의 거시적인 부분을 담당하는 리소스이다. 두 리소스 모두 하나의 포스팅을 차지하는 거대한 주제지만 대략적인 것만 설명하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwuLYH/dJMcacJp9mT/ZXBshTZhkM5H4zVppdbTL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwuLYH/dJMcacJp9mT/ZXBshTZhkM5H4zVppdbTL1/img.png&quot; data-alt=&quot;Service와 Ingress 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwuLYH/dJMcacJp9mT/ZXBshTZhkM5H4zVppdbTL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwuLYH%2FdJMcacJp9mT%2FZXBshTZhkM5H4zVppdbTL1%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;700&quot; height=&quot;452&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Service와 Ingress 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;Service&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파드 &lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot;&gt;집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 파드에게 고유한 IP 주소와 파드 집합에 대한 단일 DNS 명을 부여하고, 그것들 간에 로드-밸런스를 수행할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt; &lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때를 대비해 비슷한 조건의 파드들을 하나의 Cluster IP라고 하는, 고정적인 IP로 묶어 파드의 상태가 변경되어도 다른 파드들이 해당 서비스를 계속 이용할 수 있게 도와주는 리소스가 Service이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 이야기를 계속한다면 같은 백엔드 서비스를 제공하는 여러 개의 파드들을 Cluster IP로 묶어 고정시킨다면, 각각의 파드가 사라진다고 하더라도 다른 파드들은 같은 IP로 똑같은 백엔드 서비스를 이용할 수 있게 된다.&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;2️⃣&lt;b&gt;&lt;span&gt; Ingress&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;쿠버네티스 공식 문서를 보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;URI, 호스트네임, 경로 등과 같은 웹 개념을 이해하는 프로토콜-인지형(protocol-aware configuration) 설정 메커니즘을 이용하여 HTTP (혹은 HTTPS) 네트워크 서비스를 사용 가능하게 한다. 인그레스 개념은 쿠버네티스 API를 통해 정의한 규칙에 기반하여 트래픽을 다른 백엔드에 매핑할 수 있게 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot;&gt;클러스터 내의 서비스에 대한 &lt;b&gt;외부 접근을 관리하는 API 오브젝트&lt;/b&gt;이며, 일반적으로 HTTP를 관리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot;&gt;Service 보다 한 단계 더 위에 있는 네트워크 리소스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt; &lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;왜 필요한가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 쿠버네티스 클러스터를 통해 제공하려는 서비스의 개수가 100개라고 해보자. 만약, Service만 존재한다면 각각의 서비스에 대한 Cluster IP를 구축해야 하므로 총 100개의 IP 주소가 필요하게 된다.&amp;nbsp;만약 Ingress 없이 약 100개의 서비스를 외부로 노출하려면, 클라우드 업체(AWS 등)로부터 약 100개의 로드밸런서를 빌려야 하고 그러면 엄청난 비용이 나온다.&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;Ingress는 바로 이때 필요하다. Ingress는 외부에서 들어오는 HTTP/HTTPS 요청을 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;10&quot;&gt;도메인이나 경로(Path)에 따라 적절한 Service로 전달&lt;/b&gt;해주는 리소스이다. &lt;b&gt;L7의 로드 밸런서인 셈이다.&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;사용자의 입장에서는 외부에 Ingress의 네트워크 주소만 보고 전송하면 된다. 직접 서비스의 각기 다른 IP를 기억할 필요가 없는 것이다. Nginx의 proxy 기능을 생각하면 된다.&amp;nbsp;그리고 Ingress도 추상화된 리소스일 뿐, 실제 작업은 Ingress Controller하며 Nginx 등이 그 역할을 수행한다.&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;나중에 Service, Ingress 단독으로 쿠버네티스 네트워크에 대해 조사해야겠다.&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;a href=&quot;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775217041607&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kubernetes 서비스와 인그레스 용도구분&quot; data-og-description=&quot;이번 포스팅에서는 쿠버네티스의 서비스와 인그레스의 차이점에 대해서 알아보겠습니다. 쿠버네티스를 처음 접하는 분들은, 간단하게 API 서버를 만들어서 &amp;#96;POD&amp;#96; 를 띄우기까지는 성공하지만, 해&quot; data-og-host=&quot;danawalab.github.io&quot; data-og-source-url=&quot;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&quot; data-og-url=&quot;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/G1Ivc/dJMb8ZvBUN2/KRrUg40hSDCpxeDJB8RIs1/img.png?width=700&amp;amp;height=452&amp;amp;face=0_0_700_452&quot;&gt;&lt;a href=&quot;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://danawalab.github.io/kubernetes/2020/01/23/kubernetes-service-ingress.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/G1Ivc/dJMb8ZvBUN2/KRrUg40hSDCpxeDJB8RIs1/img.png?width=700&amp;amp;height=452&amp;amp;face=0_0_700_452');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Kubernetes 서비스와 인그레스 용도구분&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 쿠버네티스의 서비스와 인그레스의 차이점에 대해서 알아보겠습니다. 쿠버네티스를 처음 접하는 분들은, 간단하게 API 서버를 만들어서 `POD` 를 띄우기까지는 성공하지만, 해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;danawalab.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.io/ko/docs/concepts/services-networking/service/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775217048962&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;서비스&quot; data-og-description=&quot;외부와 접하는 단일 엔드포인트 뒤에 있는 클러스터에서 실행되는 애플리케이션을 노출시키며, 이는 워크로드가 여러 백엔드로 나뉘어 있는 경우에도 가능하다.&quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/&quot; data-og-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhXYI5/dJMb8RRSlv5/iUF1zyzFGccYKK9KHREFEk/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhXYI5/dJMb8RRSlv5/iUF1zyzFGccYKK9KHREFEk/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;서비스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;외부와 접하는 단일 엔드포인트 뒤에 있는 클러스터에서 실행되는 애플리케이션을 노출시키며, 이는 워크로드가 여러 백엔드로 나뉘어 있는 경우에도 가능하다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775217355718&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;인그레스(Ingress)&quot; data-og-description=&quot;URI, 호스트네임, 경로 등과 같은 웹 개념을 이해하는 프로토콜-인지형(protocol-aware configuration) 설정 메커니즘을 이용하여 HTTP (혹은 HTTPS) 네트워크 서비스를 사용 가능하게 한다. 인그레스 개념은 &quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot; data-og-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bVGEK1/dJMb9frFUUZ/JKmzYiSKhRK8KWgUEFjeY0/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373&quot;&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bVGEK1/dJMb9frFUUZ/JKmzYiSKhRK8KWgUEFjeY0/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;인그레스(Ingress)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;URI, 호스트네임, 경로 등과 같은 웹 개념을 이해하는 프로토콜-인지형(protocol-aware configuration) 설정 메커니즘을 이용하여 HTTP (혹은 HTTPS) 네트워크 서비스를 사용 가능하게 한다. 인그레스 개념은&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>DevOps/Kubernetes</category>
      <category>CNI</category>
      <category>Ingress</category>
      <category>Service</category>
      <category>쿠버네티스</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/55</guid>
      <comments>https://brotherjeantech.tistory.com/55#entry55comment</comments>
      <pubDate>Mon, 30 Mar 2026 19:00:21 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] 쿠버네티스 아키텍처(Architecture)</title>
      <link>https://brotherjeantech.tistory.com/54</link>
      <description>&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;Architecture, Minikube, kubectl, Namespace, Pod&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Architecture&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdqagF/dJMcaibHgqB/Kq1VJ7Jcmc1Q36yqjJUFyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdqagF/dJMcaibHgqB/Kq1VJ7Jcmc1Q36yqjJUFyk/img.png&quot; data-alt=&quot;공식 문서에서 설명하는 Kubernetes Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdqagF/dJMcaibHgqB/Kq1VJ7Jcmc1Q36yqjJUFyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdqagF%2FdJMcaibHgqB%2FKq1VJ7Jcmc1Q36yqjJUFyk%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;560&quot; height=&quot;352&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공식 문서에서 설명하는 Kubernetes Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 크게 클라우드의 전체적인 리소스를 관리하는 Control Plane, 실제 서비스가 동작하는 Node Plane으로 구성되어 있다. 두 부분을 합쳐 클러스터로 구성한다. 참고로 Control Plane, Node Plane 각각 클러스터로 구성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controle Plane과 Node Plane 각각에 대해 알아보자.&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;Control Plane Component&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터를 관리하는 컴포넌트 집단이다. 여러 대의 컴퓨터가 합쳐져서 각각의 컴포넌트를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤 플레인 컴포넌트를 실행하는 여러 대의 컴퓨터는 노드로써 실행되지는 않는다. 각각의 컴포넌트는 다음과 같다.&lt;/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;1️⃣ &lt;b&gt;kube-apiserver&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;쿠버네티스 클러스터를 진입하기 위한 API 서버. 클러스터의 프론트엔드이다.&lt;/li&gt;
&lt;li&gt;다른 사용자들이 클러스터에 관해 정보를 얻고 싶을 때 이용하는 API 서버다.&lt;/li&gt;
&lt;li&gt;kube-apiserver는 수평적으로 (수평적 확장, 기기의 대수를 늘린다.) 확장되도록 설계되었다. (AWS auto-scaling)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;etcd&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ etcdctl --endpoints=$ENDPOINTS --write-out=&quot;json&quot; get foo
{&quot;header&quot;:{&quot;cluster_id&quot;:289318470931837780,&quot;member_id&quot;:14947050114012957595,&quot;revision&quot;:3,&quot;raft_term&quot;:4,
&quot;kvs&quot;:[{&quot;key&quot;:&quot;Zm9v&quot;,&quot;create_revision&quot;:2,&quot;mod_revision&quot;:3,&quot;version&quot;:2,&quot;value&quot;:&quot;SGVsbG8gV29ybGQh&quot;}]}}
$
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;etcd도 클러스터로 구성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;kube-scheduler&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;어느 pod가 어느 node에서 실행할지 결정해주는 컴포넌트, 실행을 담당하는 건 아니고 그냥 이름표만 붙여주는거다.&lt;/li&gt;
&lt;li&gt;이 작업을 하기 위해선 모든 노드의 컴퓨팅 자원 현황에 대한 정보가 있어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 노드들의 자원 현황을 확인한 다음 점수를 매겨 최적의 노드를 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4️⃣ &lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;kube-controller-manager&lt;/span&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;Cloud Controller Manager랑 헷갈릴 수 있는데, 얘는 클러스터 안의 자원과 모든 데이터를 관리하는 컴포넌트다.
&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;레플리케이션 컨트롤러: 파드(Pod)의 개수가 설정한 대로 유지되는지 감시한다.&lt;/li&gt;
&lt;li&gt;엔드포인트 컨트롤러: 서비스(Service)와 파드를 연결한다.&lt;/li&gt;
&lt;li&gt;서비스 어카운트 &amp;amp; 토큰 컨트롤러: 새로운 네임스페이스에 대한 기본 계정과 API 접근 토큰을 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어느 노드가 죽었는지, 어느 노드가 살아있는지, 전체 노드 클러스터에서 원하는 수의 pod가 살아있는지, Service에 대한 파드가 연결되어 있는지 등을 확인한다.&lt;/li&gt;
&lt;li&gt;클러스터 내부에 총 책임자라 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5️⃣ &lt;b&gt;cloud-controller-manager &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;외부 클라우드 서비스 (AWS, GCS 등)과 통신하는 외부 관리용 컴포넌트이다.&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;&amp;rArr; KCM과 CCM은 동시에 돌아갈 수 있다! 클라우드 환경에서 쿠버네티스를 실행한다면, KCM이 CCM에게 자신의 역할을 일부 위임한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KCM &amp;rarr; 쿠버네티스 본연의 기능 : 파드의 갯수, 노드의 헬스 체크, 토큰 발행&lt;/li&gt;
&lt;li&gt;CCM &amp;rarr; 클라우드 API랑 통신해서 리소스 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3bQ3q/dJMcaa5MVhh/SGB3MJSBaFwrVVqK1p7UlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3bQ3q/dJMcaa5MVhh/SGB3MJSBaFwrVVqK1p7UlK/img.png&quot; data-alt=&quot;AI가 설명한 CCM, KCM 역할 비유&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3bQ3q/dJMcaa5MVhh/SGB3MJSBaFwrVVqK1p7UlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3bQ3q%2FdJMcaa5MVhh%2FSGB3MJSBaFwrVVqK1p7UlK%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;560&quot; height=&quot;287&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI가 설명한 CCM, KCM 역할 비유&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;Node Plane Component&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 단에서 실행되는 컴포넌트이다. 서비스 서버나 서비스에 필요한 여러 리소스의 실행과 관리를 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노드는 하나의 컴퓨터이다.&lt;/b&gt; CPU, RAM, SSD 등의 물리적인 자원을 가진 실제 컴퓨터이다. 이 노드 안에 수십, 수백개의 Pod들이 실행된다.&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;1️⃣ &lt;b&gt;kubelet &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;/li&gt;
&lt;li&gt;파드에서 컨테이너가 확실하게 동작하도록 관리한다.&lt;/li&gt;
&lt;li&gt;각 노드나 컴포넌트가 살아있음을 확인하는 과정을 &amp;lsquo;&lt;b&gt;헬스 체크&lt;/b&gt;&amp;rsquo;라고 하는데, 각 파드들이 잘 생성되어 있는지 헬스 체크를 한 다음, 이를 api-server로 보내 데이터를 저장한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이로 인해 etcd에 관련 데이터가 쌓이게 되고, 컨트롤 플레인에서 각 노드와 파드들에 대한 정보를 볼 수 있게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;&lt;span data-token-index=&quot;0&quot;&gt;kube-proxy&lt;/span&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;이를 이해하기 위해선 Service를 알아야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Service는 etcd에 저장된 각 노드들의 주소를 담는 객체다.&lt;/li&gt;
&lt;li&gt;사용자가 Service IP로 요청을 보내면, 컨트롤 플레인은 Service 객체를 확인해 적절한 내부 IP로 요청을 보내게 된다.&lt;/li&gt;
&lt;li&gt;전화번호부를 생각하면 쉬울 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Proxy는 Service의 요청을 받는 노드의 가장 앞단이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크의 NAT를 생각하면 쉬울 것이다, 얘가 L4 로드 밸런서이다.&lt;/li&gt;
&lt;li&gt;여기서 모든 네트워크 처리를 담당한다.&lt;/li&gt;
&lt;li&gt;또한 쿠버네티스 컨트롤 플레인의 서비스 및 엔드포인트 오브젝트의 추가와 제거를 감시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 참고로 모든 노드가 kube-proxy를 갖추고 있으며 &lt;b&gt;DNAT(분산형 NAT)&lt;/b&gt;처럼 행동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8K3Rf/dJMcaaSgOji/NGXQkkQua4ak42N2PR6yk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8K3Rf/dJMcaaSgOji/NGXQkkQua4ak42N2PR6yk1/img.png&quot; data-alt=&quot;네트워크 NAT 도식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8K3Rf/dJMcaaSgOji/NGXQkkQua4ak42N2PR6yk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8K3Rf%2FdJMcaaSgOji%2FNGXQkkQua4ak42N2PR6yk1%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;560&quot; height=&quot;322&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네트워크 NAT 도식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Minikube &amp;amp; Kubectl&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Minikube는 규모가 큰 Kubenetes 리소스를 로컬에서 돌려볼 수 있게 구성한 가상의 클러스터이다.&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;1️⃣ &lt;b&gt;Minikube&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1개의 노드 클러스터와 1개의 컨트롤 클러스터로 구성된다.&lt;/li&gt;
&lt;li&gt;쿠버네티스 미니어처라고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6NhlG/dJMcahKCxJl/OlPVaRJ5IDzvjWyG0rzdi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6NhlG/dJMcahKCxJl/OlPVaRJ5IDzvjWyG0rzdi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6NhlG/dJMcahKCxJl/OlPVaRJ5IDzvjWyG0rzdi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6NhlG%2FdJMcahKCxJl%2FOlPVaRJ5IDzvjWyG0rzdi0%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;560&quot; height=&quot;298&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;kubectl&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;사용자가 api-server 보내기 위해 작성하는 bash창이다.&lt;/li&gt;
&lt;li&gt;명령어 칠 때 치는 거라고 보면된다. 더 깊게 들어가면 사용자가 쿠버네티스 클러스터에 접속하기 위한 인터페이스이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Namespace&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터 안에서 설정하는 가상의 클러스터이다. 컴퓨터로 따지면 폴더 정도?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;네임스페이스 기반 오브젝트(Deployment, Service)에만 적용가능하며, 클러스터 범위 오브젝트(Storage Class, Node, PV)에는 적용이 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;Namespace와 DNS&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;Namespace를 할당하면 DNS가 생성된다.&lt;/li&gt;
&lt;li&gt;Kubectl에서 DNS를 통해 사용자가 네임스페이스 오브젝트에 명령을 보낼 수 있게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS 엔트리는 꼭 RFC 1123 DNS 레이블의 형식을 지켜야 한다.&lt;/li&gt;
&lt;li&gt;FQDN&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;하나의 노드 클러스터에 여러 개의 네임스페이스가 존재할 수 있다. 이럴 때 각 노드나 Pod 컴포넌트에 어떻게 접근할 수 있는지 살펴보자.&lt;/li&gt;
&lt;li&gt;예를 들어, 서비스명이 동일하지만, Namespace가 다르다면 어떻게 접근할 수 있는지 구상할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;모든 주소를 전부 적을 수도 있고 아니면 부분적으로만 적을 수 있다.&lt;br /&gt;&lt;br /&gt;1. $ curl {서비스명}.{네임스페이스명}.svc.cluster.local:{포트} &lt;br /&gt;2. $ curl {서비스명}.{네임스페이스명}.svc:{포트} &lt;br /&gt;3. $ curl {서비스명}.{네임스페이스명}:{포트} &lt;br /&gt;4. $ curl {서비스명}:{포트}&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1번은 FQDN이 사용되는 예시이다. 풀 주소를 적고 해당 컴포넌트로 요청을 보낸다.&lt;/li&gt;
&lt;li&gt;2, 3, 4 번은 Domain Chain이 사용되는 경우다. 이 경우 도메인 서버를 이용해 적절한 FQDN 주소로 바꿔 전송하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;Pod&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스의 가장 작은 배포 단위이자 관리 단위다. 쿠버네티스는 컨테이너를 하나의 단위라고 보지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 컨테이너 및 컨테이너 묶음이 하나의 Pod가 될 수 있으며 하나의 Pod를 공유하는 컨테이너들은 같은 IP, 같은 볼륨(저장소)를 가지게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ymNlP/dJMcadai6qT/Mg5Mx5JhgUXJe3auL05EaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ymNlP/dJMcadai6qT/Mg5Mx5JhgUXJe3auL05EaK/img.png&quot; data-alt=&quot;공식 문서에서 설명하는 Pod 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ymNlP/dJMcadai6qT/Mg5Mx5JhgUXJe3auL05EaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FymNlP%2FdJMcadai6qT%2FMg5Mx5JhgUXJe3auL05EaK%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;560&quot; height=&quot;443&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공식 문서에서 설명하는 Pod 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wO2k6/dJMcahKCyLI/cbKKKUXMMkG0Bc0s0uY8qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wO2k6/dJMcahKCyLI/cbKKKUXMMkG0Bc0s0uY8qK/img.png&quot; data-alt=&quot;Pod를 구성하는 하나의 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wO2k6/dJMcahKCyLI/cbKKKUXMMkG0Bc0s0uY8qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwO2k6%2FdJMcahKCyLI%2FcbKKKUXMMkG0Bc0s0uY8qK%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;560&quot; height=&quot;358&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Pod를 구성하는 하나의 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;  &lt;b&gt;Probe&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 컴포넌트 Kubelet이 각각의 컨테이너가 잘 작동하고 있는지 전송하는 컨디션 체크&lt;/li&gt;
&lt;li&gt;전에 컨트롤 컴포넌트 클러스터에서 노드가 api-server에게 자신이 살아있음을 알리는 신호를 보냈는데 그거랑 같다.&lt;/li&gt;
&lt;li&gt;Probe는 노드 컴포넌트 kubelet이 pod 안에서 실행되고 있는 각각의 컨테이너가 살아있는지 요청을 보내는 것이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/Kubernetes</category>
      <category>Architecture</category>
      <category>Kubenetes</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/54</guid>
      <comments>https://brotherjeantech.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 27 Mar 2026 13:33:14 +0900</pubDate>
    </item>
    <item>
      <title>미니 PC로 홈 서버를 만들어 보자 - 하드웨어부터 네트워크까지</title>
      <link>https://brotherjeantech.tistory.com/53</link>
      <description>&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;홈 서버를 생각하게 된 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드와 데이터 엔지니어링을 공부하면서 프로젝트의 배포는 AWS를 주로 사용해왔다. 프리 티어를 사용해도 Auto Scaling, VPN을 사용하면 꽤 많은 금액이 부과되는 것을 보면서 비용을 줄이고 싶었다. 적은 비용으로 서버를 굴릴 수 없을까 고민하던 중, 나만의 서버를 만들자는 생각이 들었고, 서버를 만드는 것이 운영체제부터 네트워크까지 다룰 수 있는 좋은 기회라 생각해 바로 진행했다.&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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;홈 서버의 종류 - 하드웨어 선택기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;미니PC로 선택한 이유&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링을 하면서 홈 서버를 여러 하드웨어로 구축할 수 있음을 알았다. 중고 데스크탑, 미니 PC 심지어 안 쓰는 노트북도 홈 서버가 될 수 있음을 알았다. 그 중 나는 미니 PC를 선택했고 프로세서는 Intel N 시리즈를 하기로 마음 먹었다.&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;했다. 미니 PC는 손바닥만하니 공간에 대한 부담이 적었다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;24시간 켜놓는 서버&lt;/b&gt;를 구성하고자 했고, 이를 위해선 저전력 프로세서가 필요했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 분해해 CPU 옆에 쿨링 써멀을 바르거나 팬을 놓고 발열을 줄일 수도 있었지만, 해당 분야는 내가 잘 몰라서 기본으로 저전력을 제공하는 프로세서를 원했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저전력이지만 Kubernetes나 ElasticSearch 같은 부피가 큰 프로그램을 돌릴 수 있어야 했기에 인텔의 N100 프로세서를 최종 스펙으로 선택했다. 더불어 RAM은 16GB, SSD는 512GB를 사기로 마음 먹었다. (중고로 팔 때 잘 팔릴 것 같기도 하고, 아니면 집에서 PC로 쓸 때 좋은 스펙이니까 이렇게 구성했다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니PC로 홈 서버를 구성한 포스트들은 대부분 중고로 15 ~ 18만원으로 미니PC를 구매했다고 했다. 근데 이게 웬 걸, 25년도 하반기가 되면서 메모리와 디스크의 가격이 급상승하기 시작했다. 새 미니PC나 중고나 가격이 너무 올라 중고가가 18만원이 기본, 20만원을 넘기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈을 최대한 아끼기 위해 당근에서 미니PC를 찾기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J9UEE/dJMcahQC6Oc/NkfTnp7EIxnocbF6tdWmD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J9UEE/dJMcahQC6Oc/NkfTnp7EIxnocbF6tdWmD0/img.png&quot; data-alt=&quot;당근에서 열심히 중고를 찾았지만 자꾸 순삭되더라..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J9UEE/dJMcahQC6Oc/NkfTnp7EIxnocbF6tdWmD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ9UEE%2FdJMcahQC6Oc%2FNkfTnp7EIxnocbF6tdWmD0%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;300&quot; height=&quot;571&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당근에서 열심히 중고를 찾았지만 자꾸 순삭되더라..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말했듯 메모리 가격이 올라서 그런지, 아니면 요즘 미니PC가 대세인지 몰라도 중고로 오라온 미니PC들이 자꾸 순삭되었다. (알림 뜨자마자 들어갔는데 이미 거래된 적도 있었음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 빨리 홈 서버를 만들고자 했기에 그냥 쿠팡에서 직구로 미니PC를 사기로 했다.&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️⃣ 최종 하드웨어&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dclmwX/dJMcahb06r7/apUELnKef9f7i1tOsZFkd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dclmwX/dJMcahb06r7/apUELnKef9f7i1tOsZFkd0/img.jpg&quot; data-alt=&quot;Ninkear 엠박스 11&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dclmwX/dJMcahb06r7/apUELnKef9f7i1tOsZFkd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdclmwX%2FdJMcahb06r7%2FapUELnKef9f7i1tOsZFkd0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;225&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ninkear 엠박스 11&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ninkear Mbox 11, N150 프로세서, 16GB, 512GB로 최종 하드웨어를 결정했다. 마침 쿠팡에서 세일 중이라 기존 N100 PC와 가격대가 비슷해졌다. 그래서 조금 무리해서 구매했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;홈 서버 운영체제 - Ubuntu 설치와 랜카드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;1️⃣&lt;b&gt;&lt;span&gt; Ubuntu 운영체제&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 운영체제는 우분투로 하기로 했다. 터미널에서 직접 서버를 다루고 싶었기 때문이다. 우분투 운영체제 버전 중 우분투 서버를 내 미니PC에 설치했다. (우분투 데스크탑이랑 헷갈렸다가 다시 설치했다.)&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;운영체제 설치를 위한 USB 준비&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 PC에 운영체제를 설치하기 위해선 BIOS 단계에서부터 운영체제 USB를 통해 설치해야한다. 이를 위해선 USB가 필요하다. USB를 컴퓨터가 인식할 수 있는 파일 형식으로 포맷한 뒤, 새 PC가 켜질 때 BIOS로 들어가는 단축키를 연타해 설치하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 USB를 준비하기 위해 SanDisk USB 64G와 balenaEtcher를 활용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://etcher.balena.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://etcher.balena.io/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767597034295&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;balenaEtcher - Flash OS images to SD cards &amp;amp; USB drives&quot; data-og-description=&quot;A cross-platform tool to flash OS images onto SD cards and USB drives safely and easily. Free and open source for makers around the world.&quot; data-og-host=&quot;etcher.balena.io&quot; data-og-source-url=&quot;https://etcher.balena.io/&quot; data-og-url=&quot;https://etcher.balena.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dy78aC/hyZQ2DTlKo/5nK2ykPDOEet2MqCTRErkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dJpbqf/hyZQ935gJ9/LoBnXj1IZZivj1gyh7s0Ok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://etcher.balena.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://etcher.balena.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dy78aC/hyZQ2DTlKo/5nK2ykPDOEet2MqCTRErkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dJpbqf/hyZQ935gJ9/LoBnXj1IZZivj1gyh7s0Ok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;balenaEtcher - Flash OS images to SD cards &amp;amp; USB drives&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A cross-platform tool to flash OS images onto SD cards and USB drives safely and easily. Free and open source for makers around the world.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;etcher.balena.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로그램을 이용해 설치 USB를 준비하면 됐다. balenaEtcher는 Validation 과정을 통해 설치된 USB가 이상이 있는지 검사한다. 문제는 balenaEtcher가 하드웨어적인 오류는 잡아내지 못했다. 첫 번째 USB가 하드웨어적으로 문제가 있었는지, 우분투를 설치하는 과정에서 checksum validation failed라 자꾸 떴다. 구글에선 USB 포트, 랜선 등 다양한 이유를 들어서 해봤는데 결국엔 내 USB 자체에 문제가 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5707&quot; data-origin-height=&quot;3255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSYdGN/dJMcahXpKah/3C3mC5KOkIXooS89kXr7I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSYdGN/dJMcahXpKah/3C3mC5KOkIXooS89kXr7I0/img.png&quot; data-alt=&quot;checksum validation failed 에러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSYdGN/dJMcahXpKah/3C3mC5KOkIXooS89kXr7I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSYdGN%2FdJMcahXpKah%2F3C3mC5KOkIXooS89kXr7I0%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;500&quot; height=&quot;285&quot; data-origin-width=&quot;5707&quot; data-origin-height=&quot;3255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;checksum validation failed 에러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 USB를 바꾸고 다시 운영체제를 설치했다.&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️⃣ rtw_8821 랜카드 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제 설치를 성공해서 좋았는데, 이번엔 'rtw_8821ce failed to send h2c command' 라는 오류 메세지가 일정 주기로 계속 뜨기 시작했다. 다른 프로세스를 실행해도 문제는 없었지만 찾아보니 무선 인터넷을 연결하기 위해선 rtw_8821ce가 필요했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 찾아본 결과 랜카드의 절전 모드 때문에 CPU와 랜카드가 서로 신호를 주고 받지 못하는 상황이었다. 당장 하드웨어 패널을 보고 랜카드를 바꿀 수 없었기에 드라이버 선에서 문제를 해결할 수 있을지 찾아봤다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5707&quot; data-origin-height=&quot;979&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsBhKE/dJMcah39XSg/aMWiyTsBhKVBfFvKK8lIJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsBhKE/dJMcah39XSg/aMWiyTsBhKVBfFvKK8lIJ0/img.png&quot; data-alt=&quot;랜카드 종류를 찾는 명령어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsBhKE/dJMcah39XSg/aMWiyTsBhKVBfFvKK8lIJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsBhKE%2FdJMcah39XSg%2FaMWiyTsBhKVBfFvKK8lIJ0%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;700&quot; height=&quot;120&quot; data-origin-width=&quot;5707&quot; data-origin-height=&quot;979&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;랜카드 종류를 찾는 명령어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rtw_8821ce 랜카드가 절전 모드로 들어갈 경우 네트워크 연결에 문제가 생김을 알았다. 외국인 개발자들은 깃허브에 개인이 올린 새로운 드라이버를 설치해 사용한다고 했다. 그래서 나도 리포지토리를 클론해 랜카드 드라이버를 교체했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/lwfinger/rtw88/issues/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/lwfinger/rtw88/issues/61&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767598123724&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;firmware failed to leave lps state &amp;middot; Issue #61 &amp;middot; lwfinger/rtw88&quot; data-og-description=&quot;Hi! The following errors constantly appear in the log. The WiFi connection is stable at the same time. июн 30 19:39:31 not-a-virus kernel: rtw_8822ce 0000:01:00.0: failed to send h2c command июн 30...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/lwfinger/rtw88/issues/61&quot; data-og-url=&quot;https://github.com/lwfinger/rtw88/issues/61&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcorZu/dJMb8Zvt2OM/QWGSPDxAMX0tgl207P6AyK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b1CjOa/dJMb8QL4HzS/CkFmLK7PrIPokdxmEG9xXK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b4CVu1/dJMb8SXqvfF/Zp8bgI8lptRFLZ3BRbRk71/img.png?width=868&amp;amp;height=1017&amp;amp;face=0_0_868_1017&quot;&gt;&lt;a href=&quot;https://github.com/lwfinger/rtw88/issues/61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/lwfinger/rtw88/issues/61&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcorZu/dJMb8Zvt2OM/QWGSPDxAMX0tgl207P6AyK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b1CjOa/dJMb8QL4HzS/CkFmLK7PrIPokdxmEG9xXK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b4CVu1/dJMb8SXqvfF/Zp8bgI8lptRFLZ3BRbRk71/img.png?width=868&amp;amp;height=1017&amp;amp;face=0_0_868_1017');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;firmware failed to leave lps state &amp;middot; Issue #61 &amp;middot; lwfinger/rtw88&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hi! The following errors constantly appear in the log. The WiFi connection is stable at the same time. июн 30 19:39:31 not-a-virus kernel: rtw_8822ce 0000:01:00.0: failed to send h2c command июн 30...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/tomaspinho/rtl8821ce&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/tomaspinho/rtl8821ce&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767597991650&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - tomaspinho/rtl8821ce&quot; data-og-description=&quot;Contribute to tomaspinho/rtl8821ce development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/tomaspinho/rtl8821ce&quot; data-og-url=&quot;https://github.com/tomaspinho/rtl8821ce&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGRQXY/hyZQ0eY0lG/7nDljS7tFDfhkoQEx4vbb0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dxOmCp/hyZQ60z5mD/RdY0nOOdbXFmCUjRPFvzV1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/tomaspinho/rtl8821ce&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/tomaspinho/rtl8821ce&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGRQXY/hyZQ0eY0lG/7nDljS7tFDfhkoQEx4vbb0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dxOmCp/hyZQ60z5mD/RdY0nOOdbXFmCUjRPFvzV1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - tomaspinho/rtl8821ce&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to tomaspinho/rtl8821ce development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3️⃣&lt;b&gt;&lt;span&gt; 방화벽 설정&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FVhzV/dJMcabQUlED/18I2za2alsiJleJOEXYgu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FVhzV/dJMcabQUlED/18I2za2alsiJleJOEXYgu0/img.png&quot; data-alt=&quot;우분투 서버 방화벽 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FVhzV/dJMcabQUlED/18I2za2alsiJleJOEXYgu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFVhzV%2FdJMcabQUlED%2F18I2za2alsiJleJOEXYgu0%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;646&quot; height=&quot;72&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우분투 서버 방화벽 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니 서버에 방화벽은 필수다. 나는 다른 기기들과 보안 연결을 하고 싶었기에 보안과 관련된 포트만 열어두고 나머지는 방화벽을 통해 포트를 잠갔다.&lt;/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;h3 style=&quot;padding: 0.6em 0.5em 0.6em 0.5em; margin: 0.5em 0em; color: #000; border-left: 10px solid #0f2443; border-bottom: 2px solid #e5e5e5; font-weight: bold;&quot; data-ke-size=&quot;size23&quot;&gt;홈 서버 네트워크 구축 - sk 모뎀부터 tailscale&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ9S7w/dJMcahQ748m/EbP0dzgLGHKz7EzSaaogD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ9S7w/dJMcahQ748m/EbP0dzgLGHKz7EzSaaogD1/img.png&quot; data-alt=&quot;내가 생각한 홈 네트워크 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ9S7w/dJMcahQ748m/EbP0dzgLGHKz7EzSaaogD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ9S7w%2FdJMcahQ748m%2FEbP0dzgLGHKz7EzSaaogD1%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;811&quot; height=&quot;157&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내가 생각한 홈 네트워크 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북으로 미니PC를 원격 접속하고 싶었기 때문에 홈 네트워크에 내 홈 서버를 등록해야 했다. 홈 네트워크에서 로컬 네트워크와 인터넷을 연결해주는 기기는 공유기라고 생각했고, 공유기에 내 홈 서버 정보를 등록한다면 외부에서 내 홈 서버로 접속할 수 있겠다는 생각을 했다.&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;1️⃣ &lt;b&gt;iptime 공유기 연결&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;문제는 홈 네트워크에 연결되면 홈 서버가 홈 네트워크에 접속할 때마다 유동 IP를 받는다는 것이었다. 내 미니 서버가 매번 네트워크에 접속할 때마다 부여받는 IP가 바뀐다면 나는 서버에 접속하기 위해 매번 다른 IP를 입력해야 했다. 그래서 나는 고정된 IP 주소를 부여해 편하게 접속하고자 했다. 이를 위해선 &lt;b&gt;DHCP&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;DHCP&lt;/b&gt; (Dynamic Host Configuration Protocol)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- &lt;/span&gt;네트워크 기기에 IP 주소, 서브넷 마스크, 게이트웨이, DNS 등 필수 네트워크 구성 정보를 자동으로 할당하는 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DHCP는 서브넷 내 IP 중 특정 IP를 기기에게 자동으로 할당해주는 프로토콜이다. 이 프로토콜을 통해 홈 서버에 특정 IP를 고정으로 연결하고자 했다.&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;내가 사용하는 공유기는 iptime이고 iptime 공유기에 접속하기 위해선 192.168.0.1에 접속하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOhkj/dJMcaioZeP5/aixGDtXxUgqh9UKFg6X6ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOhkj/dJMcaioZeP5/aixGDtXxUgqh9UKFg6X6ZK/img.png&quot; data-alt=&quot;iptime 관리자 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOhkj/dJMcaioZeP5/aixGDtXxUgqh9UKFg6X6ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOhkj%2FdJMcaioZeP5%2FaixGDtXxUgqh9UKFg6X6ZK%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;560&quot; height=&quot;334&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;iptime 관리자 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lit2U/dJMcagEIzQQ/9elKkdjkJSw56T1UVe9F6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lit2U/dJMcagEIzQQ/9elKkdjkJSw56T1UVe9F6k/img.png&quot; data-alt=&quot;DHCP 설정 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lit2U/dJMcagEIzQQ/9elKkdjkJSw56T1UVe9F6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flit2U%2FdJMcagEIzQQ%2F9elKkdjkJSw56T1UVe9F6k%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;560&quot; height=&quot;408&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DHCP 설정 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유기에 유선으로 미니 서버를 연결하면 일단 공유기가 유동 IP를 부여한다. 이 유동 IP를 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;DHCP 설정 화면에서&lt;span&gt; 고정으로 바꿔주면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;포트포워딩 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;포트포워딩은 공유기 외부에서 특정 포트로 들어온 요청을 내부 IP의 특정 포트로 연결해주는 설정이다. 나는 내 미니 서버가 허용된 특정 포트만을 이용해 통신하길 원했다. 따라서 설정을 통해 공유기의 특정 포트를 내 미니 서버의 특정 포트와 연결했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tIlrm/dJMcadHVozq/zQPkNJAgrf7fPSJrt8w7q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tIlrm/dJMcadHVozq/zQPkNJAgrf7fPSJrt8w7q0/img.png&quot; data-alt=&quot;포트포워딩 설정 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tIlrm/dJMcadHVozq/zQPkNJAgrf7fPSJrt8w7q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtIlrm%2FdJMcadHVozq%2FzQPkNJAgrf7fPSJrt8w7q0%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;560&quot; height=&quot;409&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포트포워딩 설정 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;DDNS 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 공유기에 연결하기 위해선 공유기의 IP를 알아야 한다. 설정하면서 알게된 것은 공유기의 IP도 유동 IP라는 것이었다. 처음에는 iptime 홈페이지에서 유동 IP를 받는다고 생각하고, 공유기의 IP를 고정해야겠다는 생각을 했다. 검색해보니 공유기가 유동 IP를 사용하는 것은 보안을 위해서라는 것을 알게되어 차선책인 DDNS를 생각하게 됐다.&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;DDNS는 &lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&quot;Dynamic Domain Name System&quot;의 약자로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;DNS가 서비스해야 할 대상이 동적 IP를 할당받을 경우에 DNS를 부여하기 위해 사용된다. 외부에서 공유기의 IP 대신 Domain Name을 이용해 접속할 수 있게끔 공유기가 제공하는 자체 DNS였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nIsZy/dJMcadVst9S/dyZdiLCycWvYKyrCxZaCT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nIsZy/dJMcadVst9S/dyZdiLCycWvYKyrCxZaCT1/img.png&quot; data-alt=&quot;iptime 공유기에서 제공하는 DDNS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nIsZy/dJMcadVst9S/dyZdiLCycWvYKyrCxZaCT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnIsZy%2FdJMcadVst9S%2FdyZdiLCycWvYKyrCxZaCT1%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;560&quot; height=&quot;413&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;iptime 공유기에서 제공하는 DDNS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;이렇게 하면 외부에서 저 DNS만 검색하면 내 로컬 서버에 접속할 수 있겠다고 생각했다. 그런데.. 안됐다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;홈 네트워크에서 안되는 이유를 인터넷에 찾았고, 바로 통신사 모뎀이 이유임을 알게 되었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;홈 네트워크 구성도&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYr7WI/dJMcabwBvq8/uVYpe76l1uzBOriwZCYbR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYr7WI/dJMcabwBvq8/uVYpe76l1uzBOriwZCYbR1/img.png&quot; data-alt=&quot;iptime 공유기 앞에 통신사에서 제공하는 모뎀이 있었다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYr7WI/dJMcabwBvq8/uVYpe76l1uzBOriwZCYbR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYr7WI%2FdJMcabwBvq8%2FuVYpe76l1uzBOriwZCYbR1%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;992&quot; height=&quot;137&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;iptime 공유기 앞에 통신사에서 제공하는 모뎀이 있었다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iptime을 통해 바로 인터넷으로 나가는게 아니고 그 앞에 통신사 모뎀이 있었다. 기껏 공유기 설정을 열심히 했는데 앞에 또 공유기가 있다고 하니 어이가 없었다. 그래도 앞서 공유기 설정을 했기 때문에 통신사 모뎀 설정은 얼마 걸리지 않을거라고 생각했다.&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;3️⃣ &lt;b&gt;SK 모뎀&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 모뎀에서 해줘야하는 일은 모뎀에 공유기 정보에 대한 DHCP, 포트포워딩, DDNS를 설정해 줘야 했다. 모뎀은 통신사한테 유동 IP를 받고 있을테니 DDNS는 필수였다.&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;모뎀 LAN&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PDqK3/dJMcafseWHW/VfghwMFiBI4q70frkJa5I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PDqK3/dJMcafseWHW/VfghwMFiBI4q70frkJa5I1/img.png&quot; data-alt=&quot;SK 모뎀이 담당하는 LAN DHCP 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PDqK3/dJMcafseWHW/VfghwMFiBI4q70frkJa5I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPDqK3%2FdJMcafseWHW%2FVfghwMFiBI4q70frkJa5I1%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;560&quot; height=&quot;401&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SK 모뎀이 담당하는 LAN DHCP 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모뎀 DHCP 화면에 들어가면 모뎀이 관리하는 LAN의 DHCP 설정 화면을 볼 수 있다.&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;모뎀 DDNS&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rtov4/dJMcafFM7Nb/5IWEJcfzGlVghyVdnnH3Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rtov4/dJMcafFM7Nb/5IWEJcfzGlVghyVdnnH3Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rtov4/dJMcafFM7Nb/5IWEJcfzGlVghyVdnnH3Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frtov4%2FdJMcafFM7Nb%2F5IWEJcfzGlVghyVdnnH3Kk%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;382&quot; height=&quot;242&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모뎀에서 제공하는 DDNS 서비스는 두 개가 있었는데, 문제는 두 DDNS 서비스 모두 유료였다. 한 DNS를 설정하기에 비용을 지불하는 건 아깝다고 생각했기에, DDNS 서비스 없이 접속할 수 있는 방법을 찾기 시작했다.&amp;nbsp;&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;4️⃣ &lt;b&gt;tailscale&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPN 등 여러가지 방법을 찾다가 결국 tailscale을 찾았다. tailscale은 &lt;span style=&quot;background-color: #ffffff; color: #001d35; text-align: start;&quot;&gt;기기 간 복잡한 네트워크 설정 없이, WireGuard&amp;reg; 기반으로 어디서나 안전한 P2P(Peer-to-Peer) 메쉬 VPN을 구축해 주는 무료 서비스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPN을 설정하면 여러 곳의 떨어져 있는 기기들의 가상 LAN을 구축할 수 있었다. 가장 좋았던 것은 기존의 모뎀과 공유기의 설정을 변경할 필요 없이 tailscale 서비스를 설치하면 사용할 수 있었다. 물론 내 개인 기기의 정보는 tailscale 중앙 서버에 저장해야 했지만 향후 탈퇴하면 60일 내에 없애준다고 하길래 그냥 사용했다.&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;tailscale의 가장 큰 장점은 기존 네트워크 설정을 변경하지 않아도 된다는 점이다.&amp;nbsp;일반적인 서버는 밖에서 안으로 들어오는 문을 직접 열어줘야 하지만, Tailscale은 홀 펀칭이라는 기술을 사용한다.&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;lt;AI 설명&amp;gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&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;4,0,0&quot;&gt;원리:&lt;/b&gt; 내부에 있는 기기가 먼저 외부 서버(Tailscale 서버)에 인사를 건넵니다. 이때 공유기에는 &quot;나 방금 밖에 있는 누구랑 대화했어&quot;라는 기록(세션)이 남으며 아주 잠깐 동안 문이 열립니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;연결:&lt;/b&gt; Tailscale은 이 찰나의 순간을 이용해 두 기기가 서로 직접 데이터를 주고받을 수 있도록 통로를 연결해 줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;결과:&lt;/b&gt; 사용자가 수동으로 포트를 열지 않아도, 소프트웨어가 알아서 복잡한 네트워크 환경(이중 공유기 포함)을 뚫고 터널을 만들어냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 tailscale을 내 우분투 홈 서버, 노트북에 설치했고 접속해봤다.&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;5️⃣ &lt;b&gt;연결&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PF4xm/dJMcagEIApI/k2Di0hURDwsBrfUghCcOPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PF4xm/dJMcagEIApI/k2Di0hURDwsBrfUghCcOPK/img.png&quot; data-alt=&quot;노트북에서 미니 서버로 접속 성공!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PF4xm/dJMcagEIApI/k2Di0hURDwsBrfUghCcOPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPF4xm%2FdJMcagEIApI%2Fk2Di0hURDwsBrfUghCcOPK%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;560&quot; height=&quot;447&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노트북에서 미니 서버로 접속 성공!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 연결에 성공했다.&lt;/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;미니 pc를 구매해 우분투 서버로 만들고 네트워크 설정까지 성공했다. 서버 하나를 동작하는데 많은 지식이 필요함을 느꼈다. 특히 네트워크를 공부하면서 보안 관련 이슈들을 많이 만났는데 내가 사용하는 기기들도 같은 원리로 처리가 되어있음을 실감했다. 개인 미니 서버가 생겼으니 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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>N150</category>
      <category>미니 서버</category>
      <category>미니pc</category>
      <category>홈 서버</category>
      <author>BestTomaTo</author>
      <guid isPermaLink="true">https://brotherjeantech.tistory.com/53</guid>
      <comments>https://brotherjeantech.tistory.com/53#entry53comment</comments>
      <pubDate>Mon, 9 Mar 2026 23:40:08 +0900</pubDate>
    </item>
  </channel>
</rss>