<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>악분의 블로그</title>
    <link>https://malwareanalysis.tistory.com/</link>
    <description>안녕하세요. 데브옵스 엔지니어입니다.
주어진 상황에 좋은 결과를 내기 위해 노력합니다. </description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 17:39:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>악분</managingEditor>
    <image>
      <title>악분의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/3167687/attach/a2216de5879345f1ac6188ac8a2a145b</url>
      <link>https://malwareanalysis.tistory.com</link>
    </image>
    <item>
      <title>Kubernetes v1.36 업그레이드 전에 확인할 운영 영향과 핸즈온</title>
      <link>https://malwareanalysis.tistory.com/929</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 kubernetes v1.36 릴리즈 노트를 읽고, 패치 내용 요약과 몇 가지 핸즈온을 더한 글입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;기능-요약&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;새로운 기능&lt;/h1&gt;
&lt;table style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;&lt;b&gt;MutatingAdmissionPolicy&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CEL 기반 in-process mutation 정책&lt;/td&gt;
&lt;td&gt;단순 label, field, default injection은 webhook보다 운영 부담이 적음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;&lt;b&gt;User Namespaces for pods&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컨테이너 root를 host 비권한 UID/GID로 매핑&lt;/td&gt;
&lt;td&gt;multi-tenant 환경과 breakout 방어에 유리하지만 runtime, volume,&lt;span&gt;&amp;nbsp;&lt;/span&gt;securityContext&lt;span&gt;&amp;nbsp;&lt;/span&gt;호환성 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;Fine-grained kubelet API authorization&lt;/td&gt;
&lt;td&gt;kubelet API 권한을&lt;span&gt;&amp;nbsp;&lt;/span&gt;nodes/proxy보다 세밀하게 제어&lt;/td&gt;
&lt;td&gt;monitoring agent 권한을 least privilege로 줄일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;VolumeGroupSnapshot&lt;/td&gt;
&lt;td&gt;여러 PVC를 crash-consistent하게 snapshot&lt;/td&gt;
&lt;td&gt;CSI driver와 snapshot controller 지원이 전제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;Mutable CSINode allocatable&lt;/td&gt;
&lt;td&gt;CSI driver가 node별 volume attach limit을 동적으로 갱신&lt;/td&gt;
&lt;td&gt;volume attach limit이 바뀌는 환경에서 scheduling 실패를 줄임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;DRA prioritized alternatives&lt;/td&gt;
&lt;td&gt;DRA device request에 우선순위 기반 fallback 가능&lt;/td&gt;
&lt;td&gt;GPU 모델 fallback 같은 AI/HPC scheduling에 유용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;PSI metrics on cgroup v2&lt;/td&gt;
&lt;td&gt;kubelet이 CPU, memory, I/O stall time을 node, pod, container 수준으로 노출&lt;/td&gt;
&lt;td&gt;단순 사용률보다 node contention 분석에 도움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;td&gt;MemoryQoS with cgroup v2&lt;/td&gt;
&lt;td&gt;memory.high,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memory.min,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memory.low로 pod QoS class별 memory 보호를 조정&lt;/td&gt;
&lt;td&gt;kernel, runtime, kubelet 설정이 맞는 node에서만 실험적으로 검토&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;Resource health status&lt;/td&gt;
&lt;td&gt;pod status에서 device health 확인&lt;/td&gt;
&lt;td&gt;GPU/가속기 장애 원인 추적에 도움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;Strict IP/CIDR validation&lt;/td&gt;
&lt;td&gt;잘못된 IP/CIDR 입력을 더 엄격하게 검증&lt;/td&gt;
&lt;td&gt;오래된 manifest에 비정상 값이 있으면 경고나 실패 가능성 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;.kuberc&lt;/td&gt;
&lt;td&gt;cluster config와 kubectl 사용자 preference 분리&lt;/td&gt;
&lt;td&gt;개인 CLI 설정과 cluster 접근 설정을 분리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;DRA device taints/tolerations&lt;/td&gt;
&lt;td&gt;node taint처럼 device 상태를 scheduling에 반영&lt;/td&gt;
&lt;td&gt;가속기 장애, 점검, 격리 운영에 유용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;Constrained Impersonation&lt;/td&gt;
&lt;td&gt;impersonation 권한을 수행 가능 action과 함께 제한&lt;/td&gt;
&lt;td&gt;controller 권한 위임 리스크 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beta&lt;/td&gt;
&lt;td&gt;/statusz,&lt;span&gt;&amp;nbsp;&lt;/span&gt;/flagz&lt;/td&gt;
&lt;td&gt;component 상태와 실행 flag를 HTTP endpoint로 확인&lt;/td&gt;
&lt;td&gt;장애 대응 때 control plane 설정 확인이 쉬워짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;td&gt;HPA scale to zero&lt;/td&gt;
&lt;td&gt;external/object metric 기반으로 replica 0까지 축소&lt;/td&gt;
&lt;td&gt;비용 절감 가능성이 있지만 feature gate와 metric 설계가 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;td&gt;Manifest-based admission control config&lt;/td&gt;
&lt;td&gt;admission policy를 API object가 아니라 static file에서 로드&lt;/td&gt;
&lt;td&gt;etcd 장애나 policy 삭제 공격에도 admission 보호 가능성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha&lt;/td&gt;
&lt;td&gt;Native histogram metrics&lt;/td&gt;
&lt;td&gt;kube-apiserver 등에서 sparse histogram 기반 metric 실험&lt;/td&gt;
&lt;td&gt;SLI/SLO 해상도 개선 가능성, metric backend 호환성 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;영향도&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;변경되는&amp;nbsp; 기능&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가장 눈에 띄는 변경은&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service.spec.externalIPs&lt;span&gt;&amp;nbsp;&lt;/span&gt;deprecation&lt;/b&gt;입니다. v1.36부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;externalIPs를 사용하는 service를 생성하거나 수정하면 warning이 표시됩니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공식 릴리즈 글과 KEP-5707 기준으로 kube-proxy의&lt;span&gt;&amp;nbsp;&lt;/span&gt;externalIPs&lt;span&gt;&amp;nbsp;&lt;/span&gt;지원은 단계적으로 제거될 예정입니다. v1.40에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;AllowServiceExternalIPs&lt;span&gt;&amp;nbsp;&lt;/span&gt;feature gate 기본값이&lt;span&gt;&amp;nbsp;&lt;/span&gt;false로 바뀌고, v1.43에서는 feature gate가 잠기며 관련 구현이 제거될 예정입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bicdYu/dJMcajozhUT/0JlQgvGh4BjBjgt07EdSu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bicdYu/dJMcajozhUT/0JlQgvGh4BjBjgt07EdSu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bicdYu/dJMcajozhUT/0JlQgvGh4BjBjgt07EdSu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbicdYu%2FdJMcajozhUT%2F0JlQgvGh4BjBjgt07EdSu1%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;1910&quot; height=&quot;120&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;120&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 외에도 일부 메트릭과 플러그인 동작이 변경되었습니다.&lt;/p&gt;
&lt;table style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ACTION REQUIRED&lt;/td&gt;
&lt;td&gt;volume_operation_total_errors&lt;span&gt;&amp;nbsp;&lt;/span&gt;metric 이름이&lt;span&gt;&amp;nbsp;&lt;/span&gt;volume_operation_errors_total로 변경&lt;/td&gt;
&lt;td&gt;Prometheus alert, Grafana dashboard, recording rule이 깨질 수 있음&lt;/td&gt;
&lt;td&gt;모니터링 repo에서 기존 metric 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACTION REQUIRED&lt;/td&gt;
&lt;td&gt;scheduler PreBind plugin 병렬 실행 인터페이스 변경&lt;/td&gt;
&lt;td&gt;custom scheduler plugin이 있으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;PreBindPreFlightResult&lt;span&gt;&amp;nbsp;&lt;/span&gt;대응 필요&lt;/td&gt;
&lt;td&gt;사내 scheduler plugin 코드 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACTION REQUIRED&lt;/td&gt;
&lt;td&gt;DRA ResourceClaim status update RBAC 세분화&lt;/td&gt;
&lt;td&gt;DRA driver/controller가 403을 만날 수 있음&lt;/td&gt;
&lt;td&gt;DRA 사용 cluster의 ClusterRole 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACTION REQUIRED&lt;/td&gt;
&lt;td&gt;kubeadm flex-volume 통합 지원 제거&lt;/td&gt;
&lt;td&gt;kubeadm이 더 이상 KCM static pod에 flex-volume 경로를 자동 mount하지 않음&lt;/td&gt;
&lt;td&gt;flex-volume 사용 여부 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACTION REQUIRED&lt;/td&gt;
&lt;td&gt;etcd_bookmark_counts&lt;span&gt;&amp;nbsp;&lt;/span&gt;metric 이름이&lt;span&gt;&amp;nbsp;&lt;/span&gt;etcd_bookmark_total로 변경&lt;/td&gt;
&lt;td&gt;etcd/API server 관련 alert와 dashboard 수정 필요&lt;/td&gt;
&lt;td&gt;모니터링 repo에서 기존 metric 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deprecation&lt;/td&gt;
&lt;td&gt;Service.spec.externalIPs&lt;span&gt;&amp;nbsp;&lt;/span&gt;deprecated&lt;/td&gt;
&lt;td&gt;v1.36부터 warning이 발생하고, KEP-5707 기준 kube-proxy 지원이 단계적으로 제거될 예정&lt;/td&gt;
&lt;td&gt;kubectl get svc -A -o yaml에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;externalIPs&lt;span&gt;&amp;nbsp;&lt;/span&gt;검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removed/Disabled&lt;/td&gt;
&lt;td&gt;gitRepo&lt;span&gt;&amp;nbsp;&lt;/span&gt;volume plugin 비활성화, 다시 켤 수 없음&lt;/td&gt;
&lt;td&gt;gitRepo&lt;span&gt;&amp;nbsp;&lt;/span&gt;volume을 쓰는 pod가 더 이상 정상 동작하지 않음&lt;/td&gt;
&lt;td&gt;manifest에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;gitRepo:&lt;span&gt;&amp;nbsp;&lt;/span&gt;검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;td&gt;in-tree Portworx volume plugin 제거&lt;/td&gt;
&lt;td&gt;Portworx in-tree 경로 의존 cluster는 CSI migration 상태 확인 필요&lt;/td&gt;
&lt;td&gt;PV/StorageClass provisioner 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;td&gt;cAdvisor의&lt;span&gt;&amp;nbsp;&lt;/span&gt;container_cpu_load_average_10s,&lt;span&gt;&amp;nbsp;&lt;/span&gt;container_cpu_load_d_average_10s,&lt;span&gt;&amp;nbsp;&lt;/span&gt;cpu_tasks_state&lt;span&gt;&amp;nbsp;&lt;/span&gt;metric 제거&lt;/td&gt;
&lt;td&gt;사용 중인 dashboard panel이 빈 값이 될 수 있음&lt;/td&gt;
&lt;td&gt;Prometheus query 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;실습&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;실습&lt;/h1&gt;
&lt;h2 id=&quot;쿠버네티스-설치&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;쿠버네티스 설치&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Docker 컨테이너로 실습을 진행했고, &lt;b&gt;k3d로 kubernetes 클러스터를 설치&lt;/b&gt;했습니다. Docker image 버전은 2026년 5월 10일 기준 최신 이미지를 선택했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 Docker image:&lt;span&gt;&amp;nbsp;&lt;/span&gt;rancher/k3s:v1.36.0-k3s1&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;k3d 클러스터를 설치하려면 k3d CLI가 있어야 합니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# MacBook
curl -L -o /tmp/k3d-darwin-arm64 https://github.com/k3d-io/k3d/releases/download/v5.8.3/k3d-darwin-arm64
chmod +x /tmp/k3d-darwin-arm64
/tmp/k3d-darwin-arm64 version&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;k3d CLI로 클러스터를 생성합니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;k3d cluster create k8s-136 \
  --image rancher/k3s:v1.36.0-k3s1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;클러스터-삭제&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;클러스터 삭제&lt;/h2&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;k3d cluster delete k8s-136&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;user-namespace&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;user namespace&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;user namespace는 컨테이너의 root UID가 host의 root UID를 직접 사용하지 못하게 하는 기&lt;/b&gt;능입니다. user namespace를 사용하면 컨테이너 내부의 root UID는 host에서 root가 아니라 별도의 UID로 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1628&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXwUVd/dJMcaaSNnmr/q3u1eOBJ8QkGc8jWOa9VW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXwUVd/dJMcaaSNnmr/q3u1eOBJ8QkGc8jWOa9VW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXwUVd/dJMcaaSNnmr/q3u1eOBJ8QkGc8jWOa9VW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXwUVd%2FdJMcaaSNnmr%2Fq3u1eOBJ8QkGc8jWOa9VW1%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;1628&quot; height=&quot;122&quot; data-origin-width=&quot;1628&quot; data-origin-height=&quot;122&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;user namespace를 사용하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;hostUsers: false를 설정하면 됩니다.&lt;/p&gt;
&lt;div id=&quot;cb4&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: userns-root-pod
spec:
  hostUsers: false
  containers:
    - name: shell
      image: busybox:1.36.1
      command:
        - sh
        - -c
        - id &amp;amp;&amp;amp; sleep 3600
      securityContext:
        runAsUser: 0
        runAsGroup: 0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제약사항은 공식 문서에서 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user namespace 제약사항:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#limitations&quot;&gt;https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/#limitations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;눈에 띄는 제약사항은 NFS 볼륨을 마운트할 때 user namespace를 사용할 수 없다는 점입니다. user namespace를 사용하더라도&lt;span&gt;&amp;nbsp;&lt;/span&gt;idmap mounts&lt;span&gt;&amp;nbsp;&lt;/span&gt;기능으로 컨테이너 내부에서는 고정된 UID 권한으로 파일을 사용할 수 있습니다. 하지만 NFS는 아직&lt;span&gt;&amp;nbsp;&lt;/span&gt;idmap mounts를 지원하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;externalips-deprecated-확인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;externalIPs deprecated 확인&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;externalIPs가 설정된 service를 생성하면 warning 메시지를 볼 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb5&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: deprecated-external-ip-demo
  labels:
    app.kubernetes.io/name: deprecated-external-ip-demo
spec:
  type: ClusterIP
  # externalIPs 설정
  externalIPs:
    - 203.0.113.10
  selector:
    app.kubernetes.io/name: external-ip-demo
  ports:
    - name: http
      port: 80
      targetPort: 8080
      protocol: TCP&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAggt6/dJMcacQz87y/CUP0pqdKfLiDYrIoxyv4Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAggt6/dJMcacQz87y/CUP0pqdKfLiDYrIoxyv4Q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAggt6/dJMcacQz87y/CUP0pqdKfLiDYrIoxyv4Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAggt6%2FdJMcacQz87y%2FCUP0pqdKfLiDYrIoxyv4Q1%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;1910&quot; height=&quot;120&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;mutatingadmissionpolicy&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;MutatingAdmissionPolicy&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존에는 admission controller에서 mutation을 적용하려면 webhook과 webhook 요청을 처리할 API server가 필요했습니다. &lt;b&gt;v1.36부터는 webhook server 없이&lt;span&gt;&amp;nbsp;&lt;/span&gt;MutatingAdmissionPolicy와&lt;span&gt;&amp;nbsp;&lt;/span&gt;MutatingAdmissionPolicyBinding&lt;span&gt;&amp;nbsp;&lt;/span&gt;API resource로 단순 mutation을 정의&lt;/b&gt;할 수 있습니다. mutation 동작원리가 궁금하신 분은 이전 글을 참고바랍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- admission controller 동작원리: &lt;a href=&quot;https://malwareanalysis.tistory.com/704&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://malwareanalysis.tistory.com/704&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778409983519&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;쿠버네티스 Admission controller&quot; data-og-description=&quot;1. Admission controller이란? 쿠버네티스 Admission controller는 말 그대로 Admission 기능을 수행합니다. Admission이라는 영어 단어는 허가를 의미하며, 쿠버네티스 세계에서 Admission은, 쿠버네티스 요청을 수&quot; data-og-host=&quot;malwareanalysis.tistory.com&quot; data-og-source-url=&quot;https://malwareanalysis.tistory.com/704&quot; data-og-url=&quot;https://malwareanalysis.tistory.com/704&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FOorP/dJMb8WMt3MA/UcDPm7zuLGVXg72yFVNJZ0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/CfEFt/dJMb8QeqB5T/LZqJmZqKAqV8lXOh9DymKK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bLGvKj/dJMb8SXChlF/L65qlmVMjwZ7p9C2KLGzsK/img.png?width=1278&amp;amp;height=476&amp;amp;face=0_0_1278_476&quot;&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/704&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://malwareanalysis.tistory.com/704&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FOorP/dJMb8WMt3MA/UcDPm7zuLGVXg72yFVNJZ0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/CfEFt/dJMb8QeqB5T/LZqJmZqKAqV8lXOh9DymKK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bLGvKj/dJMb8SXChlF/L65qlmVMjwZ7p9C2KLGzsK/img.png?width=1278&amp;amp;height=476&amp;amp;face=0_0_1278_476');&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;쿠버네티스 Admission controller&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Admission controller이란? 쿠버네티스 Admission controller는 말 그대로 Admission 기능을 수행합니다. Admission이라는 영어 단어는 허가를 의미하며, 쿠버네티스 세계에서 Admission은, 쿠버네티스 요청을 수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;malwareanalysis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 예제는 pod가 생성되면 label을 추가하는 mutation을 정의합니다.&lt;/p&gt;
&lt;div id=&quot;cb6&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: add-release-label.example.com
  labels:
    app.kubernetes.io/name: k8s-136-release-notes
spec:
  matchConstraints:
    resourceRules:
      - apiGroups:
          - &quot;&quot;
        apiVersions:
          - v1
        operations:
          - CREATE
        resources:
          - pods
  matchConditions:
    - name: skip-if-release-label-exists
      expression: &quot;!has(object.metadata.labels) || !('release.kubernetes.io/tested-version' in object.metadata.labels)&quot;
  failurePolicy: Fail
  reinvocationPolicy: Never
  mutations:
    - patchType: JSONPatch
      jsonPatch:
        expression: &amp;gt;
          !has(object.metadata.labels) ?
          [
            JSONPatch{
              op: &quot;add&quot;,
              path: &quot;/metadata/labels&quot;,
              value: {&quot;release.kubernetes.io/tested-version&quot;: &quot;v1.36&quot;}
            }
          ] :
          [
            JSONPatch{
              op: &quot;add&quot;,
              path: &quot;/metadata/labels/&quot; + jsonpatch.escapeKey(&quot;release.kubernetes.io/tested-version&quot;),
              value: &quot;v1.36&quot;
            }
          ]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;cb7&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
  name: add-release-label-binding.example.com
  labels:
    app.kubernetes.io/name: k8s-136-release-notes
spec:
  policyName: add-release-label.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 pod를 생성하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;MutatingAdmissionPolicy와&lt;span&gt;&amp;nbsp;&lt;/span&gt;MutatingAdmissionPolicyBinding&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정 때문에 pod에 label이 추가됩니다.&lt;/p&gt;
&lt;div id=&quot;cb8&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: map-sample-pod
  labels:
    app.kubernetes.io/name: map-sample
spec:
  restartPolicy: Always
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.10
      resources:
        requests:
          cpu: 5m
          memory: 16Mi
        limits:
          cpu: 50m
          memory: 64Mi&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhy9kw/dJMcagFuvDu/G7gmbkTBHLQXQu873q1m8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhy9kw/dJMcagFuvDu/G7gmbkTBHLQXQu873q1m8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhy9kw/dJMcagFuvDu/G7gmbkTBHLQXQu873q1m8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhy9kw%2FdJMcagFuvDu%2FG7gmbkTBHLQXQu873q1m8K%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;1618&quot; height=&quot;498&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubernetes v1.36 릴리즈 블로그:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/&quot;&gt;https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kubernetes v1.36 changelog:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md#changelog-since-v1350&quot;&gt;https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md#changelog-since-v1350&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kubernetes cgroup v2 공식 문서:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/architecture/cgroups/&quot;&gt;https://kubernetes.io/docs/concepts/architecture/cgroups/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kubernetes PSI metrics 공식 문서:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/docs/reference/instrumentation/understand-psi-metrics/&quot;&gt;https://kubernetes.io/docs/reference/instrumentation/understand-psi-metrics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kubernetes v1.36 MemoryQoS 블로그:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/blog/2026/04/29/kubernetes-v1-36-memory-qos-tiered-protection/&quot;&gt;https://kubernetes.io/blog/2026/04/29/kubernetes-v1-36-memory-qos-tiered-protection/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;kubernetes release page:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/releases/&quot;&gt;https://kubernetes.io/releases/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;k3s v1.36.0+k3s1 release:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/k3s-io/k3s/releases/tag/v1.36.0%2Bk3s1&quot;&gt;https://github.com/k3s-io/k3s/releases/tag/v1.36.0%2Bk3s1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;k3d release:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/k3d-io/k3d/releases&quot;&gt;https://github.com/k3d-io/k3d/releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MetalBear Kubernetes 1.36 정리:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://metalbear.com/blog/kubernetes-1-36/&quot;&gt;https://metalbear.com/blog/kubernetes-1-36/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>kubernetes</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/929</guid>
      <comments>https://malwareanalysis.tistory.com/929#entry929comment</comments>
      <pubDate>Sun, 10 May 2026 19:46:36 +0900</pubDate>
    </item>
    <item>
      <title>스터디 챕터 8 정리  - LLM As Judge, AI Agent</title>
      <link>https://malwareanalysis.tistory.com/928</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;챕터 8은 아래 두가지 주제를 다룹니다.&lt;br /&gt;1. 수많은 AI모델을 선택하기 위해 AI모델을 평가하는 과정&lt;br /&gt;2. 나의 회사에 상황에 맞는 AI agent를 만드는 과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;예제-1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;예제 1&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 1은 AI 모델에 따라 프롬프트의 응답이 다르므로, 평가 지표를 만들어 모델을 평가하고 선택하라는 의미를 담고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 1 실습 과정은 같은 질문 3개를 3개 모델에 던지고, GPT-4o가 채점 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1980&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EFoKt/dJMcaf7xd7b/Tuta5uPFku2MlRVavno7W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EFoKt/dJMcaf7xd7b/Tuta5uPFku2MlRVavno7W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EFoKt/dJMcaf7xd7b/Tuta5uPFku2MlRVavno7W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEFoKt%2FdJMcaf7xd7b%2FTuta5uPFku2MlRVavno7W0%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;1980&quot; height=&quot;852&quot; data-origin-width=&quot;1980&quot; data-origin-height=&quot;852&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제의 평가 결과는 10.0 만점 기준이고 JSON으로 저장됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo4SHE/dJMcaarEZi6/W7UKRZD2dQJkZXh4uqmj6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo4SHE/dJMcaarEZi6/W7UKRZD2dQJkZXh4uqmj6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo4SHE/dJMcaarEZi6/W7UKRZD2dQJkZXh4uqmj6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo4SHE%2FdJMcaarEZi6%2FW7UKRZD2dQJkZXh4uqmj6K%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;1216&quot; height=&quot;756&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;756&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;h1 id=&quot;부록.-claude가-공개한-ai-agent-평가-시스템&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;부록. Claude가 공개한 AI Agent 평가 시스템&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2026년 1월&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1a1a1a;&quot; href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Claude 기술 블로그&lt;/a&gt;에서는 AI 에이전트 평가 시스템을 설명합니다. 평가 시스템이 왜 필요하고 어떻게 평가하는지 기술합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2244&quot; data-origin-height=&quot;2116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HS7Gd/dJMcacpt7ga/DZ83Xj1FbcOoyqFZl2GAW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HS7Gd/dJMcacpt7ga/DZ83Xj1FbcOoyqFZl2GAW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HS7Gd/dJMcacpt7ga/DZ83Xj1FbcOoyqFZl2GAW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHS7Gd%2FdJMcacpt7ga%2FDZ83Xj1FbcOoyqFZl2GAW0%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;2244&quot; height=&quot;2116&quot; data-origin-width=&quot;2244&quot; data-origin-height=&quot;2116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtccM/dJMcadBPRAB/cvNZ9BwQkwRgCz5gjKmiU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtccM/dJMcadBPRAB/cvNZ9BwQkwRgCz5gjKmiU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtccM/dJMcadBPRAB/cvNZ9BwQkwRgCz5gjKmiU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwtccM%2FdJMcadBPRAB%2FcvNZ9BwQkwRgCz5gjKmiU0%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;2106&quot; height=&quot;1102&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;2358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSeHFO/dJMcahK5hXi/LdkppQNNKcoxxqBFa4FveK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSeHFO/dJMcahK5hXi/LdkppQNNKcoxxqBFa4FveK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSeHFO/dJMcahK5hXi/LdkppQNNKcoxxqBFa4FveK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSeHFO%2FdJMcahK5hXi%2FLdkppQNNKcoxxqBFa4FveK%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;1106&quot; height=&quot;2358&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;2358&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 예제처럼 AI의 결과를 AI가 평가하는 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;LLM as a Judge&lt;/b&gt;라고 합니다. Claude 기술 블로그를 보면 AI 모델은 자기가 작성한 코드를 평가할 때 관대하다는 표현이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;예제-2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;예제 2&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 2는 AI Agent를 만드는 예제입니다. 책에서는 AI engine이라고 표현했으나 2026년 5월 기준으로는 AI agent가 더 맞는 표현인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;책에서는 Agent 이름을 copilot이라고 부릅니다. copilot은 사용자의 질문의 의도를 이해하고 필요한 네트워크 장비 컨텍스트를 이해하는게 목적입니다. copilot은 사용자에게 요청을 받으면, 사용자 메시지를 분석하여 프롬프트를 만들어 AI 모델에 요청합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4318&quot; data-origin-height=&quot;1868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3aijE/dJMcag6s3Ej/T0y4BKNZH71bBDiowJ1W21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3aijE/dJMcag6s3Ej/T0y4BKNZH71bBDiowJ1W21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3aijE/dJMcag6s3Ej/T0y4BKNZH71bBDiowJ1W21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3aijE%2FdJMcag6s3Ej%2FT0y4BKNZH71bBDiowJ1W21%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;4318&quot; height=&quot;1868&quot; data-origin-width=&quot;4318&quot; data-origin-height=&quot;1868&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;copilot은 아래 구조로 프롬프트를 만듭니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4900&quot; data-origin-height=&quot;1826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgb3Ej/dJMb990BEon/tIwkoWw0w38z74QZRaomzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgb3Ej/dJMb990BEon/tIwkoWw0w38z74QZRaomzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgb3Ej/dJMb990BEon/tIwkoWw0w38z74QZRaomzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgb3Ej%2FdJMb990BEon%2FtIwkoWw0w38z74QZRaomzK%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;4900&quot; height=&quot;1826&quot; data-origin-width=&quot;4900&quot; data-origin-height=&quot;1826&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자 프롬프트를 만드는 과정이 예제 2의 핵심입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 메시지를 분석하여 의도를 분류&lt;/li&gt;
&lt;li&gt;사용자 메시지를 분석하여, 필요하면 회사 네트워크 정보가 담긴 파일 정보를 로드&lt;/li&gt;
&lt;li&gt;AI 모델이 답변을 잘할 수 있도록 few-shot 예제 파일을 로드&lt;/li&gt;
&lt;li&gt;사용자 메시지를 마지막에 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구현한 코드를 보면 조립한 프롬프트가 어떻게 구성되어 있는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;1866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcjfGt/dJMcaarEZoB/VkgMJQU14hw5oXRJc1KUQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcjfGt/dJMcaarEZoB/VkgMJQU14hw5oXRJc1KUQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcjfGt/dJMcaarEZoB/VkgMJQU14hw5oXRJc1KUQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcjfGt%2FdJMcaarEZoB%2FVkgMJQU14hw5oXRJc1KUQ1%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;1758&quot; height=&quot;1866&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;1866&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자 프롬프트를 만들기 위해 필요한 코드는 함수로 구현되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2192&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjGjOx/dJMcacXhCBe/4AghlbLMhKW74cZyLFEw10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjGjOx/dJMcacXhCBe/4AghlbLMhKW74cZyLFEw10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjGjOx/dJMcacXhCBe/4AghlbLMhKW74cZyLFEw10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjGjOx%2FdJMcacXhCBe%2F4AghlbLMhKW74cZyLFEw10%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;2192&quot; height=&quot;778&quot; data-origin-width=&quot;2192&quot; data-origin-height=&quot;778&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아쉬운 점은 사용자 메시지 분석 로직이 if~else로 되어 있어서 정확도가 떨어진다는 것입니다. 아래는 의도를 분류하는 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AWuzC/dJMcafsWkaQ/LFrzaK4s1NSHcLgUQ8FMLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AWuzC/dJMcafsWkaQ/LFrzaK4s1NSHcLgUQ8FMLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AWuzC/dJMcafsWkaQ/LFrzaK4s1NSHcLgUQ8FMLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAWuzC%2FdJMcafsWkaQ%2FLFrzaK4s1NSHcLgUQ8FMLk%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;1988&quot; height=&quot;554&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;554&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;회사 네트워크 컨텍스트는 아래처럼 JSON으로 설정되어 있습니다. 사용자 메시지를 if문으로 분석해서 회사 장비 이름이 있으면 사용자 프롬프트에 컨텍스트 정보를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqD3Ho/dJMcafsWkaY/5nkh6NPdFD7TWQKnTfJeCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqD3Ho/dJMcafsWkaY/5nkh6NPdFD7TWQKnTfJeCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqD3Ho/dJMcafsWkaY/5nkh6NPdFD7TWQKnTfJeCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqD3Ho%2FdJMcafsWkaY%2F5nkh6NPdFD7TWQKnTfJeCK%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;1988&quot; height=&quot;554&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;554&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 의도와 전혀 상관없는 질문을 하더라도, AI 모델이 의도한 방향으로 답변하도록 유도합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2540&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxY4SK/dJMb990BEoH/cczgSygSalMrKKbYzLyY10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxY4SK/dJMb990BEoH/cczgSygSalMrKKbYzLyY10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxY4SK/dJMb990BEoH/cczgSygSalMrKKbYzLyY10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxY4SK%2FdJMb990BEoH%2FcczgSygSalMrKKbYzLyY10%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;2540&quot; height=&quot;656&quot; data-origin-width=&quot;2540&quot; data-origin-height=&quot;656&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제가 의도한 대로 잘 동작하면, AI 모델 답변과 함께 회사 정보(Working)도 출력합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;2448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qDRkX/dJMcaaFc2fJ/DRE7EG8g0KrKWhNCok91aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qDRkX/dJMcaaFc2fJ/DRE7EG8g0KrKWhNCok91aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qDRkX/dJMcaaFc2fJ/DRE7EG8g0KrKWhNCok91aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqDRkX%2FdJMcaaFc2fJ%2FDRE7EG8g0KrKWhNCok91aK%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;1830&quot; height=&quot;2448&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;2448&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;h1 id=&quot;예제-3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;예제 3&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 3은 예제 2에서 사용자 프롬프트가 더 늘었고 조건문도 함께 늘었습니다. 사용자 프롬프트에는 더 많은 네트워크 컨텍스트가 추가되었고, 설정 작업일 때 영향 범위를 분석해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2866&quot; data-origin-height=&quot;1870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dD5tXx/dJMb99TNtWH/Y9bBCq54SYkvtQG16s7Tz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dD5tXx/dJMb99TNtWH/Y9bBCq54SYkvtQG16s7Tz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dD5tXx/dJMb99TNtWH/Y9bBCq54SYkvtQG16s7Tz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdD5tXx%2FdJMb99TNtWH%2FY9bBCq54SYkvtQG16s7Tz1%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;2866&quot; height=&quot;1870&quot; data-origin-width=&quot;2866&quot; data-origin-height=&quot;1870&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프롬프트를 만드는 과정을 보면 enhanced_context와 Impact Analysis가 추가되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CWPbf/dJMcafT2gTz/VrNbL3sAPHTA2KJkezWC70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CWPbf/dJMcafT2gTz/VrNbL3sAPHTA2KJkezWC70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CWPbf/dJMcafT2gTz/VrNbL3sAPHTA2KJkezWC70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCWPbf%2FdJMcafT2gTz%2FVrNbL3sAPHTA2KJkezWC70%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;1988&quot; height=&quot;1870&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1870&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;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI Networking Cookbook 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/PacktPublishing/AI-Networking-Cookbook-First-Edition&quot;&gt;https://github.com/PacktPublishing/AI-Networking-Cookbook-First-Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Ai</category>
      <category>스터디</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/928</guid>
      <comments>https://malwareanalysis.tistory.com/928#entry928comment</comments>
      <pubDate>Tue, 5 May 2026 13:33:05 +0900</pubDate>
    </item>
    <item>
      <title>스터디 챕터 7 정리 - 프러덕션을 위해, Streamlit을 FastAPI로 마이그레이션</title>
      <link>https://malwareanalysis.tistory.com/927</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 스터디에서 공부한 내용을 정리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주제: 챕터 7은 챕터 6에서 만든 Streamlit으로 만든 웹앱을 프로덕션으로 사용하기 위해 FastAPI로 마이그레이션 하는 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;왜-streamlit을-프로덕션에-사용하지-않는가&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;왜 Streamlit을 프로덕션에 사용하지 않는가?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 Streamlit 사용 경험이 많지 않아서, 이번 스터디를 준비하면서 조사한 내용을 정리했습니다. 여기서 제가 말하는 프로덕션은 &amp;ldquo;내 노트북에서 데모로 실행하는 것&amp;rdquo;이 아니라, 돈을 받고 사용자에게 서비스하는 상황을 기준으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Streamlit는 여러 한계 때문에 프로덕션에 사용하지 않습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 전체 스크립트 재실행 구조&lt;br /&gt;Streamlit은 사용자가 버튼을 누르거나 입력값을 바꾸면 스크립트를 다시 실행하는 방식으로 동작합니다. 작은 데모에서는 이 구조가 편합니다. 그런데 LLM 호출, 데이터베이스 저장, 외부 API 호출이 섞이면 &amp;ldquo;내가 원하지 않은 코드가 다시 실행되는 상황&amp;rdquo; 이 발생하면서 원치 않는 현상이 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 긴 작업을 다루는 방식&lt;br /&gt;AI 서비스에서는 임베딩 생성, LLM 추론, 로그 저장처럼 시간이 오래 걸리는 작업이 자주 나옵니다. 짧으면 몇 초지만, 길면 몇십 초 이상 걸릴 수도 있습니다. FastAPI는 ASGI(Asynchronous Server Gateway Interface)를 기반으로 동작해서 이런 웹 API 서버 구조를 만들기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. API 생성&lt;br /&gt;Streamlit은  API를 만드는 기능을 제공하지 않지만, FastAPI는 처음부터 HTTP API를 만들기 위한 프레임워크입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 운영구조로 확장&lt;br /&gt;프로덕션으로 가면 인증, rate limit, timeout, retry, logging, tracing, autoscaling 같은 주제가 따라옵니다. Streamlit에서는 목적 자체가 빠르게 기능을 보여주는 것이기 때문에, 운영을 위한 기능은 없습니다. 또한 오토스케일링이 발생했을때 여전히 비지니스 로직이 잘 동작하는지 검증된 자료를 찾을 수 없었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;책에서-선택한-프로덕션-방법은&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;책에서 선택한 프로덕션 방법은?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;책에서는 프러덕션으로 배포하기 위해 Streamlit을 FastAPI로 변경했습니다. FastAPI는 파이썬으로 API와 html을 만들 수 있는 장점이 있지만, 제 생각은 비동기 지원이 AI관련 프로덕션 API에 좋은 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FastAPI는 eventloop가 구현되어 있어서 비동기에 친화적&lt;/b&gt;입니다. API가 실행하는 로직에서 임베딩, 추론 등이 있을때 최소 몇초에서 몇십분 latency가 길어집니다. 이런 로직에 비동기가 지원되지 않으면 다른 요청이 block되어서 대기해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다른 웹 프레임워크인 Django는 비동기를 부분지원하고 있습니다. 또한, 인터페이스가 ASGI가 아니여서 튜닝이 필요합니다. 아래 블로그에서 AI서비스를 하면서 Django에서 FastAPI로 왜 옮겼는지에 대한 설명이 있으니 참고바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비동기 이외에 FastAPI는 Pydantic를 적극 사용하고 있어 데이터 관리가 쉽고, FastAPI CLI, swagger 지원, 검색자료가 많음 등의 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;fastapi-아키텍처-간단히-살펴보기&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;FastAPI 아키텍처 간단히 살펴보기&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FastAPI의 아키텍처 핵심은 ASGI입니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;FastAPI는 ASGI 프레임워크인 starlette를 쉽게 사용할 수 있도록 감싼 것입니다. 그리고 ASGI server 역할을 하는 uvicorn이 FastAPI를 구동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7vbz2/dJMcadPmWqy/z4rgJVScfA0GZOog9wigJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7vbz2/dJMcadPmWqy/z4rgJVScfA0GZOog9wigJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7vbz2/dJMcadPmWqy/z4rgJVScfA0GZOog9wigJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7vbz2%2FdJMcadPmWqy%2Fz4rgJVScfA0GZOog9wigJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;632&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;632&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ASGI가 등장하기 전에는 파이썬 진영에서 WSGI를 사용했습니다. 하지만 WSGI는 비동기를 처리하는 데 한계가 있었고, ASGI server 중 하나인 uvicorn은 uvloop를 사용해 비동기 처리를 해결했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;프로덕션에서 FastAPI 성능을 최적화&lt;/b&gt;하려면, 애플리케이션 코드(async 처리 패턴 등)와 uvicorn/gunicorn 설정을 튜닝해야 합니다. guicorn은 uvicorn 워커 프로세스를 관리하는 프로세스 매니저입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;더 자세한 FastAPI 아키텍처는 아래 블로그를 참고하면 좋습니다.&lt;br /&gt;&lt;a href=&quot;https://m.blog.naver.com/pjt3591oo/222772705407&quot;&gt;- https://m.blog.naver.com/pjt3591oo/222772705407&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777878697969&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;[fastapi] uvicorn, fastapi 비동기 메커니즘 이해&quot; data-og-description=&quot;안녕하세요 멍개입니다. 최근에 MLOps를 공부하면서 새로운 스택을 다뤄보고 싶어서 fastapi를 다뤄보게...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://m.blog.naver.com/pjt3591oo/222772705407&quot; data-og-url=&quot;https://blog.naver.com/pjt3591oo/222772705407&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JO9Vy/dJMb8UHSUeO/Oj9KeKOLM7RbfYcWmR8w1k/img.png?width=387&amp;amp;height=414&amp;amp;face=0_0_387_414&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/pjt3591oo/222772705407&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://m.blog.naver.com/pjt3591oo/222772705407&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JO9Vy/dJMb8UHSUeO/Oj9KeKOLM7RbfYcWmR8w1k/img.png?width=387&amp;amp;height=414&amp;amp;face=0_0_387_414');&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;[fastapi] uvicorn, fastapi 비동기 메커니즘 이해&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요 멍개입니다. 최근에 MLOps를 공부하면서 새로운 스택을 다뤄보고 싶어서 fastapi를 다뤄보게...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.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;h1 id=&quot;실습&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;실습&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실습 예제는 저의 github에 공개되어 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- github 주소: &lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/study_coobok/chapter7&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/study_coobok/chapter7&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;파이썬-패키지-관리-uv&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;파이썬 패키지 관리: uv&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트와 Python 패키지 관리를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;uv를 사용했습니다. 책에는 없는 내용이지만  프로젝트와 패키지를 관리하는 도구는 프로덕션에 필수여서 추가했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;환경변수-생성&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경변수 생성&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;.env파일을 만들고&lt;span&gt;&amp;nbsp;&lt;/span&gt;OPENAI_API_KEY를 넣습니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;cp .env.example .env&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;도커-컨테이너-생성&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;도커 컨테이너 생성&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; docker compose 명령어로 컨테이너를 실행합니다. 컨테이너 실행 과정에 docker build가 같이 실행됩니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;예제-1번&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예제 1번&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 1번은 2개 기능을 구현했습니다.&lt;br /&gt;- &amp;ldquo;/ask&amp;rdquo; API생성&lt;br /&gt;- 사용자 요청과 사용자에게 줄 응답을 Pydantic로 관리&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;1242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDGGgi/dJMcaaLW5UW/X437WaKudVKmRVml1UszmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDGGgi/dJMcaaLW5UW/X437WaKudVKmRVml1UszmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDGGgi/dJMcaaLW5UW/X437WaKudVKmRVml1UszmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDGGgi%2FdJMcaaLW5UW%2FX437WaKudVKmRVml1UszmK%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;3320&quot; height=&quot;1242&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;1242&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 vscode에서 http extension을 사용하여 테스트를 진행했습니다. http extension은 http파일을 postman처럼 API를 요청하고 응답을 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3090&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfinlB/dJMcahEgPAH/KunrsRKm9KQlIRo4CWFPT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfinlB/dJMcahEgPAH/KunrsRKm9KQlIRo4CWFPT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfinlB/dJMcahEgPAH/KunrsRKm9KQlIRo4CWFPT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfinlB%2FdJMcahEgPAH%2FKunrsRKm9KQlIRo4CWFPT1%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;3090&quot; height=&quot;902&quot; data-origin-width=&quot;3090&quot; data-origin-height=&quot;902&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;swagger로도 테스트 가능합니다. question에 질문할 내용을 적고 Execute버튼을 누르면 API응답을 받을 수 있습니다.&lt;br /&gt;- 접속주소:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1a1a1a;&quot; href=&quot;http://localhost:8001/&quot;&gt;http://localhost:8001/&lt;/a&gt;&lt;br /&gt;- 질문내용: Configure OSPF area 0&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3312&quot; data-origin-height=&quot;1820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C68R8/dJMcagekAo4/F4h2UJ3lxJpkfX6KMcbPk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C68R8/dJMcagekAo4/F4h2UJ3lxJpkfX6KMcbPk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C68R8/dJMcagekAo4/F4h2UJ3lxJpkfX6KMcbPk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC68R8%2FdJMcagekAo4%2FF4h2UJ3lxJpkfX6KMcbPk0%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;3312&quot; height=&quot;1820&quot; data-origin-width=&quot;3312&quot; data-origin-height=&quot;1820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3132&quot; data-origin-height=&quot;1376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCOTIV/dJMcajouU6J/pswTQV2LavCy5AXYbgkpK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCOTIV/dJMcajouU6J/pswTQV2LavCy5AXYbgkpK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCOTIV/dJMcajouU6J/pswTQV2LavCy5AXYbgkpK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCOTIV%2FdJMcajouU6J%2FpswTQV2LavCy5AXYbgkpK1%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;3132&quot; height=&quot;1376&quot; data-origin-width=&quot;3132&quot; data-origin-height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;예제-2번&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예제 2번&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두번째 예제는 예제 1번에서 디바이스 타입을 받도록 API가 수정되었습니다.&lt;br /&gt;- Pydantic에서 input model에서 device_type 추가&lt;br /&gt;- 시스템프롬프트 관리 변수 추가&lt;br /&gt;- 질문가능한 device 목록을 보여주는 API 추가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2848&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c39box/dJMcajouU7r/iy8GwsbXqc8w0ogIFXKjOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c39box/dJMcajouU7r/iy8GwsbXqc8w0ogIFXKjOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c39box/dJMcajouU7r/iy8GwsbXqc8w0ogIFXKjOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc39box%2FdJMcajouU7r%2Fiy8GwsbXqc8w0ogIFXKjOk%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;2848&quot; height=&quot;1130&quot; data-origin-width=&quot;2848&quot; data-origin-height=&quot;1130&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;/devices&amp;rdquo; API를 호출하면 질문가능한 device목록이 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xBMOx/dJMcagZE42i/6koFKKmd080f4cniQrmVN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xBMOx/dJMcagZE42i/6koFKKmd080f4cniQrmVN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xBMOx/dJMcagZE42i/6koFKKmd080f4cniQrmVN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxBMOx%2FdJMcagZE42i%2F6koFKKmd080f4cniQrmVN1%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;2318&quot; height=&quot;766&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;766&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; cisco로 device_type을 지정하여 &amp;ldquo;/ask&amp;rdquo;API를 호출하면, cisco에 맞는 응답을 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2706&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPtC9L/dJMcaciGROt/lQ3Kqc7kj3IPJQqYPwt0a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPtC9L/dJMcaciGROt/lQ3Kqc7kj3IPJQqYPwt0a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPtC9L/dJMcaciGROt/lQ3Kqc7kj3IPJQqYPwt0a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPtC9L%2FdJMcaciGROt%2FlQ3Kqc7kj3IPJQqYPwt0a1%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;2706&quot; height=&quot;830&quot; data-origin-width=&quot;2706&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;예제-3번&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예제 3번&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 3번은 사용자의 요청을 DB에 저장하고 조회하는 기능이 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 책은 간단한 예제여서 DB라이브러리에 대해 설명은 하지 않습니다. sqlalchemy라이브러리로 DB세션을 간단히 관리하고 Pydantic 으로 ORM로 DML을 제어합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Pydantic은 API 요청/응답처럼  스키마 관리를 class로 관리(ORM)할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3126&quot; data-origin-height=&quot;1070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSxget/dJMcagZE42N/CDkJ6Gt15q2D9GUZMCsIE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSxget/dJMcagZE42N/CDkJ6Gt15q2D9GUZMCsIE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSxget/dJMcagZE42N/CDkJ6Gt15q2D9GUZMCsIE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSxget%2FdJMcagZE42N%2FCDkJ6Gt15q2D9GUZMCsIE1%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;3126&quot; height=&quot;1070&quot; data-origin-width=&quot;3126&quot; data-origin-height=&quot;1070&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 예제에서는 SessionLocal로 DB세션을 생성해서 API에서 필요할 때 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fdc0z/dJMcaaSI24c/JpwOleK7vhSTSlkrZsdBy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fdc0z/dJMcaaSI24c/JpwOleK7vhSTSlkrZsdBy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fdc0z/dJMcaaSI24c/JpwOleK7vhSTSlkrZsdBy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFdc0z%2FdJMcaaSI24c%2FJpwOleK7vhSTSlkrZsdBy0%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;2550&quot; height=&quot;1148&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 3번에서 &amp;ldquo;/ask&amp;rdquo; API요청을 실행할 때 마다 sqlite에 요청과 AI가 생성한 응답이 저장됩니다. 그리고 &amp;ldquo;/hisotry&amp;rdquo;API를 호출하면 지금까지 요청과 생성한 응답을 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2248&quot; data-origin-height=&quot;1210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKa1uj/dJMcajhLMtp/7YE6FgJfWcKP6cVlPV7Ug0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKa1uj/dJMcajhLMtp/7YE6FgJfWcKP6cVlPV7Ug0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKa1uj/dJMcajhLMtp/7YE6FgJfWcKP6cVlPV7Ug0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKa1uj%2FdJMcajhLMtp%2F7YE6FgJfWcKP6cVlPV7Ug0%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;2248&quot; height=&quot;1210&quot; data-origin-width=&quot;2248&quot; data-origin-height=&quot;1210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;예제-4번&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예제 4번&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제 4번은 Jinja 템플릿을 사용하여 html페이지와 FastAPI의 API를 연동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQnG2d/dJMcadoktRI/uYz1LmFSKgBRhfC5wK0331/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQnG2d/dJMcadoktRI/uYz1LmFSKgBRhfC5wK0331/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQnG2d/dJMcadoktRI/uYz1LmFSKgBRhfC5wK0331/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQnG2d%2FdJMcadoktRI%2FuYz1LmFSKgBRhfC5wK0331%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;2404&quot; height=&quot;1176&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;1172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/puK6g/dJMcadaLbBh/GB3c07mwm6Ik7Hy5lmbDKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/puK6g/dJMcadaLbBh/GB3c07mwm6Ik7Hy5lmbDKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/puK6g/dJMcadaLbBh/GB3c07mwm6Ik7Hy5lmbDKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpuK6g%2FdJMcadaLbBh%2FGB3c07mwm6Ik7Hy5lmbDKk%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;1888&quot; height=&quot;1172&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;1172&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 예제는 html이 파이썬 코드에 하드코딩되어 있어서, html은 파일로 추출했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b48axD/dJMcaayqarR/nJd9SfCzufOktKQ2ZHsB51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b48axD/dJMcaayqarR/nJd9SfCzufOktKQ2ZHsB51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b48axD/dJMcaayqarR/nJd9SfCzufOktKQ2ZHsB51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb48axD%2FdJMcaayqarR%2FnJd9SfCzufOktKQ2ZHsB51%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;2146&quot; height=&quot;544&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;544&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI는 HTMLResponse class를 사용하여 HTML포맷으로 사용자에게 html응답을 줄 수 있습니다. html의 API요청은 결국 javascript로 http요청을 하기 때문에, 기존 API를 따로 수정하지 않아도 됩니다. 다만, 이 예제에서는 구분을 쉽게하기 위해 &amp;ldquo;/web-ask&amp;rdquo;라는 API를 생성했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DFds9/dJMcacC0vNf/PXRQUp3bcYkONJWzkRoiak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DFds9/dJMcacC0vNf/PXRQUp3bcYkONJWzkRoiak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DFds9/dJMcacC0vNf/PXRQUp3bcYkONJWzkRoiak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDFds9%2FdJMcacC0vNf%2FPXRQUp3bcYkONJWzkRoiak%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;2348&quot; height=&quot;812&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;812&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;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://buildsmartengineering.substack.com/p/fastapi-vs-django-in-2026-i-moved&quot;&gt;https://buildsmartengineering.substack.com/p/fastapi-vs-django-in-2026-i-moved&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/&quot;&gt;https://fastapi.tiangolo.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/bigger-applications/&quot;&gt;https://fastapi.tiangolo.com/tutorial/bigger-applications/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pydantic.dev/&quot;&gt;https://docs.pydantic.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.sqlalchemy.org/en/20/orm/quickstart.html&quot;&gt;https://docs.sqlalchemy.org/en/20/orm/quickstart.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/PacktPublishing/AI-Networking-Cookbook-First-Edition&quot;&gt;https://github.com/PacktPublishing/AI-Networking-Cookbook-First-Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.naver.com/pjt3591oo/222772705407&quot;&gt;https://blog.naver.com/pjt3591oo/222772705407&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://starlette.dev/&quot;&gt;https://starlette.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://asgi.readthedocs.io/en/latest/&quot;&gt;https://asgi.readthedocs.io/en/latest/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Ai</category>
      <category>스터디</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/927</guid>
      <comments>https://malwareanalysis.tistory.com/927#entry927comment</comments>
      <pubDate>Mon, 4 May 2026 16:16:05 +0900</pubDate>
    </item>
    <item>
      <title>AI 모델 사용 vs AI 에이전트 사용 &amp;mdash; LangGraph  실습</title>
      <link>https://malwareanalysis.tistory.com/926</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI Codex 팀이 공개한 에이전트 루프 문서 를 공부하면서, AI모델을 사용하는 것과 AI Agentg를 사용하는 것의 차이를 정리했습니다.&lt;br /&gt;- codex 문서: -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://openai.com/index/unrolling-the-codex-agent-loop/&quot;&gt;https://openai.com/index/unrolling-the-codex-agent-loop/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실습자료는 저의 github에 공개되어 있습니다.&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/langgraph-agent-loop&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/langgraph-agent-loop&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;이-글에서-다루는-것&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;이 글에서 다루는 것&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 모델을 직접 호출하는 것과 AI 에이전트를 사용하는 것이 어떻게 다른지 비교합니다.&lt;/li&gt;
&lt;li&gt;AI 모델만 사용했을 때 부딪히는 한계(기억 부재, 도구 사용 불가, 단발성 요청)를 정리합니다.&lt;/li&gt;
&lt;li&gt;AI 에이전트의 핵심 기능인 Loop와 Tools를 LangGraph 예제를 만듭니다.&lt;/li&gt;
&lt;li&gt;kuberntes pod의 잘못된 이미지 태그 오류(ImagePullBackOff)를 디버깅하는  AI Agent를 만들어봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ai-모델&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AI 모델&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 모델을 사용한다는 것은 모델에게 요청을 보내고 모델이 생성한 답변을 받는 것을 말합니다.&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;2890&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XrUI2/dJMcabYnk8s/44S0Udx1LeXTphWMiWjQbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XrUI2/dJMcabYnk8s/44S0Udx1LeXTphWMiWjQbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XrUI2/dJMcabYnk8s/44S0Udx1LeXTphWMiWjQbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXrUI2%2FdJMcabYnk8s%2F44S0Udx1LeXTphWMiWjQbk%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;2890&quot; height=&quot;732&quot; data-origin-width=&quot;2890&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangGraph로 AI 모델만 사용하는 코드는 매우 간단합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모델 선택&lt;/li&gt;
&lt;li&gt;모델에게 사용자 요청 전달&lt;/li&gt;
&lt;li&gt;모델의 응답 수신&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래는 LangChain&lt;span&gt;&amp;nbsp;&lt;/span&gt;init_chat_model로 모델을 호출하는 예제입니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
import os

load_dotenv()

def main():
    llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;))
    question = &quot;오늘 서울 날씨 어때?&quot;
    print(f&quot;[USER] {question}&quot;)
    response = llm.invoke(question)
    print(f&quot;[ASSISTANT] {response.content}&quot;)

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델 호출과 응답 과정을 LangSmith로 추적하면 요청과 응답 span 1개만 보입니다. 호출 흐름이 단순합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2740&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1CGC/dJMcaaZruyS/Mjd8K5G253W6FMkdkziRW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1CGC/dJMcaaZruyS/Mjd8K5G253W6FMkdkziRW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1CGC/dJMcaaZruyS/Mjd8K5G253W6FMkdkziRW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1CGC%2FdJMcaaZruyS%2FMjd8K5G253W6FMkdkziRW0%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;2740&quot; height=&quot;720&quot; data-origin-width=&quot;2740&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ai-모델만-사용하는-것의-한계&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AI 모델만 사용하는 것의 한계&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델만 사용하면 요청에 대한 응답을 한 번 생성하는 기능밖에 못합니다. 그래서 프로덕션에 그대로 쓰기에는 한계가 있습니다. 대표적인 한계 세 가지를 정리하면 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이전 요청을 기억하지 못합니다. AI 모델은 현재 요청만 알 뿐 이전 대화 맥락을 가지고 있지 않습니다.&lt;/li&gt;
&lt;li&gt;인터넷 검색이나 파일 시스템 접근 같은 행위는 AI 모델 혼자서 할 수 없습니다.&lt;/li&gt;
&lt;li&gt;모든 요청이 단발성입니다. AI 모델 호출은 한 번에 끝나기 때문에 꼬리질문을 이어가는 식의 진행이 어렵습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 한계를 극복하려고 사용자의 요청을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;AI 모델이 더 잘 해결하도록 감싸는 소프트웨어가 등장했습니다. 이 소프트웨어를 AI 에이전트라고 부릅니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;챗 AI 인터페이스, CLI 도구(Claude Code, Codex CLI) 등이 모두 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;3236&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cumEwc/dJMcaayo4nU/jnBquSKY2touFkv9t2Ktck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cumEwc/dJMcaayo4nU/jnBquSKY2touFkv9t2Ktck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cumEwc/dJMcaayo4nU/jnBquSKY2touFkv9t2Ktck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcumEwc%2FdJMcaayo4nU%2FjnBquSKY2touFkv9t2Ktck%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;3236&quot; height=&quot;906&quot; data-origin-width=&quot;3236&quot; data-origin-height=&quot;906&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ai-에이전트의-대표-기능-loop&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AI 에이전트의 대표 기능: Loop&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트의 대표 기능 중 하나는 Loop입니다. 앞에서 설명했듯 AI 모델 호출은 단발성이라 모델이 사용자에게 추가 정보를 요구하거나 스스로 후속 작업을 이어갈 수 없습니다. AI 에이전트는 AI 모델이 &amp;ldquo;끝났다&amp;rdquo;고 신호할 때까지 모델과 요청, 응답을 반복합니다. 이 반복 과정이 Loop입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Codex CLI 문서에서는 AI 에이전트와 AI 모델이 한 맥락으로 주고받는 한 번의 대화를 turn이라고 부르고, turn이 여러 번 발생할 때 아래처럼 반복된다고 설명합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btPfN9/dJMcafGtCS3/bT0Uogfe938MUrhoSFa9Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btPfN9/dJMcafGtCS3/bT0Uogfe938MUrhoSFa9Q1/img.png&quot; data-alt=&quot;출처: &amp;amp;nbsp; https://openai.com/index/unrolling-the-codex-agent-loop/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btPfN9/dJMcafGtCS3/bT0Uogfe938MUrhoSFa9Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtPfN9%2FdJMcafGtCS3%2FbT0Uogfe938MUrhoSFa9Q1%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;1822&quot; height=&quot;1148&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: &amp;nbsp; https://openai.com/index/unrolling-the-codex-agent-loop/&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Loop는 직접 만들 수도 있습니다. 2026년 5월 기준으로 LangGraph로 Loop를 만드는 학습 자료가 많습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex02_weather_agent.py#L41-L49&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex02_weather_agent.py#L41-L49&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;from langgraph.graph import StateGraph, START, END

graph = (
    StateGraph(State)
    .add_node(&quot;call_model&quot;, call_model)
    .add_node(&quot;tools&quot;, ToolNode(tools))
    .add_edge(START, &quot;call_model&quot;)
    .add_conditional_edges(&quot;call_model&quot;, should_continue, {&quot;tools&quot;: &quot;tools&quot;, END: END})
    .add_edge(&quot;tools&quot;, &quot;call_model&quot;)
    .compile()
)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드의 Loop는 다음과 같이 동작합니다. AI 에이전트가 사용자 요청을 AI 모델에 전달합니다. AI 모델이 도구 사용이 필요하다고 응답하면 AI 에이전트가 도구를 실행하고 그 결과를 모델에게 다시 전달합니다. AI 모델이 도구가 더 이상 필요 없다고 판단하면 Loop를 종료합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N9HAF/dJMcaiXrkOm/MQjojcij7Zk692xunXJGA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N9HAF/dJMcaiXrkOm/MQjojcij7Zk692xunXJGA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N9HAF/dJMcaiXrkOm/MQjojcij7Zk692xunXJGA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN9HAF%2FdJMcaiXrkOm%2FMQjojcij7Zk692xunXJGA1%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;1462&quot; height=&quot;1374&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Loop의 실제 예제는 다음 챕터인 Tools에서 같이 살펴봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ai-에이전트의-대표-기능-tools&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AI 에이전트의 대표 기능: Tools&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델은 학습된 데이터 범위 밖의 내용은 답변을 생성하지 못합니다. 또는 답을 만들더라도 잘못된 정보를 만들어내는 할루시네이션이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학습 데이터가 2025년 12월 31일까지라면, 오늘인 2026년 5월 1일의 날씨를 물어봤을 때 모델은 모른다고 답합니다.&lt;br /&gt;- 예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex02_weather_agent.py&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex02_weather_agent.py&lt;/a&gt;&lt;/p&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;))
question = &quot;오늘 서울 날씨 어때?&quot;
print(f&quot;[USER] {question}&quot;)
response = llm.invoke(question)
print(f&quot;[ASSISTANT] {response.content}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4272&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bynWGM/dJMcaf7vEEX/jGuQNMNxBBBaKIifYmqr3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bynWGM/dJMcaf7vEEX/jGuQNMNxBBBaKIifYmqr3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bynWGM/dJMcaf7vEEX/jGuQNMNxBBBaKIifYmqr3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbynWGM%2FdJMcaf7vEEX%2FjGuQNMNxBBBaKIifYmqr3k%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;4272&quot; height=&quot;194&quot; data-origin-width=&quot;4272&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 모델이 자신이 모르는 영역을 확인할 수 있는 수단이 필요한데, 그 수단이 바로 Tools입니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;AI 모델에게 요청할 때 사용 가능한 도구 목록을 함께 알려줘야 합니다. LangGraph에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;bind_tools로 어떤 도구를 사용할 수 있는지 모델에 전달합니다.&lt;/p&gt;
&lt;div id=&quot;cb4&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;)).bind_tools(tools)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;도구는 직접 코드로 작성할 수도 있고, Skills나 MCP처럼 표준화된 도구 인터페이스를 사용할 수도 있습니다. 아래 코드는 서울 날씨를 조회하는 도구를 직접 작성한 예입니다.&lt;/p&gt;
&lt;div id=&quot;cb5&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from langchain_core.tools import tool

@tool
def get_seoul_weather() -&amp;gt; dict | str:
    &quot;&quot;&quot;서울의 현재 날씨를 반환한다. 기온(섭씨), 풍속, 날씨 코드를 포함.&quot;&quot;&quot;
    url = &quot;https://api.open-meteo.com/v1/forecast&quot;
    params = {&quot;latitude&quot;: 37.5665, &quot;longitude&quot;: 126.9780, &quot;current_weather&quot;: &quot;true&quot;}
    try:
        r = httpx.get(url, params=params, timeout=10)
        r.raise_for_status()
        return r.json()[&quot;current_weather&quot;]
    except (httpx.HTTPError, KeyError, ValueError) as e:
        return f&quot;ERROR: open-meteo 호출 실패 &amp;mdash; {type(e).__name__}: {e}&quot;


llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;)).bind_tools(tools)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드를 실행하면 AI 에이전트는 Loop로 동작합니다. AI 모델이 AI 에이전트에게&lt;span&gt;&amp;nbsp;&lt;/span&gt;get_seoul_weather&lt;span&gt;&amp;nbsp;&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;3060&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0rWU5/dJMb99MYLeO/EDIBK7ZHCKswumGBuf2Vg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0rWU5/dJMb99MYLeO/EDIBK7ZHCKswumGBuf2Vg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0rWU5/dJMb99MYLeO/EDIBK7ZHCKswumGBuf2Vg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0rWU5%2FdJMb99MYLeO%2FEDIBK7ZHCKswumGBuf2Vg1%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;3060&quot; height=&quot;866&quot; data-origin-width=&quot;3060&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;get_seoul_weather&lt;span&gt;&amp;nbsp;&lt;/span&gt;도구를 실행해 날씨를 조회하고, 그 결과를 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;3126&quot; data-origin-height=&quot;1526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq4HtZ/dJMcaiXrkQq/lmQaWl5gXyklyQMXpE4AL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq4HtZ/dJMcaiXrkQq/lmQaWl5gXyklyQMXpE4AL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq4HtZ/dJMcaiXrkQq/lmQaWl5gXyklyQMXpE4AL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq4HtZ%2FdJMcaiXrkQq%2FlmQaWl5gXyklyQMXpE4AL1%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;3126&quot; height=&quot;1526&quot; data-origin-width=&quot;3126&quot; data-origin-height=&quot;1526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;kubernetes-pod-오류를-진단하는-ai-에이전트-구현&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;kubernetes pod 오류를 진단하는 AI 에이전트 구현&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일부러 잘못된 nginx 이미지 태그를 사용하는 pod를 만들고, AI 에이전트가 이미지 문제를 디버깅하는 예제입니다.&lt;/p&gt;
&lt;div id=&quot;cb6&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spec:
  containers:
    - name: nginx
      image: nginx:1.99-totally-fake-tag&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGvShP/dJMcacbTQL9/vIKwk1yFt2IrbaGJiEQ8Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGvShP/dJMcacbTQL9/vIKwk1yFt2IrbaGJiEQ8Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGvShP/dJMcacbTQL9/vIKwk1yFt2IrbaGJiEQ8Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGvShP%2FdJMcacbTQL9%2FvIKwk1yFt2IrbaGJiEQ8Sk%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;1440&quot; height=&quot;174&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;174&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;ai-모델만-사용한-경우&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AI 모델만 사용한 경우&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 아닌 AI 모델만 호출하면 현재 상황을 디버깅하지 못하고 일반적인 디버깅 절차만 알려줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex03_k8s_model.py&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex03_k8s_model.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;cb7&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;))
question = &quot;내 nginx 파드가 안 떠요. ErrImagePull인 것 같은데 디버깅 좀 해줘.&quot;
print(f&quot;[USER] {question}&quot;)
response = llm.invoke(question)
print(f&quot;[ASSISTANT] {response.content}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4224&quot; data-origin-height=&quot;2108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5rYOT/dJMcadojpsS/9yeqBjAGetbdw4K4wgQGk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5rYOT/dJMcadojpsS/9yeqBjAGetbdw4K4wgQGk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5rYOT/dJMcadojpsS/9yeqBjAGetbdw4K4wgQGk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5rYOT%2FdJMcadojpsS%2F9yeqBjAGetbdw4K4wgQGk0%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;4224&quot; height=&quot;2108&quot; data-origin-width=&quot;4224&quot; data-origin-height=&quot;2108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangSmith로 추적하면 OpenAI 모델 요청과 응답 1개만 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKGN4G/dJMb99MYLfk/c0y10HtE45SaobS5NfTy20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKGN4G/dJMb99MYLfk/c0y10HtE45SaobS5NfTy20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKGN4G/dJMb99MYLfk/c0y10HtE45SaobS5NfTy20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKGN4G%2FdJMb99MYLfk%2Fc0y10HtE45SaobS5NfTy20%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;1888&quot; height=&quot;326&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;ai-에이전트를-사용한-경우&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AI 에이전트를 사용한 경우&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangGraph로 Loop를 구성하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;kubectl을 사용할 수 있는 도구를 만들었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex04_k8s_agent.py&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/langgraph-agent-loop/src/ex04_k8s_agent.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;cb8&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;graph = (
    StateGraph(State)
    .add_node(&quot;call_model&quot;, call_model)
    .add_node(&quot;tools&quot;, ToolNode(tools))
    .add_edge(START, &quot;call_model&quot;)
    .add_conditional_edges(&quot;call_model&quot;, should_continue, {&quot;tools&quot;: &quot;tools&quot;, END: END})
    .add_edge(&quot;tools&quot;, &quot;call_model&quot;)
    .compile()
)

@tool
def kubectl_get_pods() -&amp;gt; str:
    &quot;&quot;&quot;default namespace의 모든 Pod 목록과 상태를 반환한다.&quot;&quot;&quot;
    return _run([&quot;kubectl&quot;, &quot;get&quot;, &quot;pods&quot;, &quot;-o&quot;, &quot;wide&quot;])

@tool
def kubectl_describe_pod(name: str) -&amp;gt; str:
    &quot;&quot;&quot;특정 Pod의 상세 정보(이벤트 포함)를 반환한다.&quot;&quot;&quot;
    return _run([&quot;kubectl&quot;, &quot;describe&quot;, &quot;pod&quot;, name])

@tool
def kubectl_get_events() -&amp;gt; str:
    &quot;&quot;&quot;default namespace의 최근 이벤트를 시간순으로 반환한다.&quot;&quot;&quot;
    return _run([&quot;kubectl&quot;, &quot;get&quot;, &quot;events&quot;, &quot;--sort-by=.lastTimestamp&quot;])

llm = init_chat_model(os.getenv(&quot;LLM_MODEL&quot;, &quot;openai:gpt-4o-mini&quot;)).bind_tools(tools)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델은 도구를 사용해 pod 상태를 확인하고 오류 원인을 정확하게 찾았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4178&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mH7Oe/dJMcaaSHWA2/dZ8wMO4hVMbnuKQnuQQ9kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mH7Oe/dJMcaaSHWA2/dZ8wMO4hVMbnuKQnuQQ9kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mH7Oe/dJMcaaSHWA2/dZ8wMO4hVMbnuKQnuQQ9kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmH7Oe%2FdJMcaaSHWA2%2FdZ8wMO4hVMbnuKQnuQQ9kk%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;4178&quot; height=&quot;1258&quot; data-origin-width=&quot;4178&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangSmith를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;kubectl get pod와&lt;span&gt;&amp;nbsp;&lt;/span&gt;kubectl describe pod&lt;span&gt;&amp;nbsp;&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;1840&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csJnaT/dJMcabD34OH/ZjrQWRtEq1JID0f721TMq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csJnaT/dJMcabD34OH/ZjrQWRtEq1JID0f721TMq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csJnaT/dJMcabD34OH/ZjrQWRtEq1JID0f721TMq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsJnaT%2FdJMcabD34OH%2FZjrQWRtEq1JID0f721TMq1%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;1840&quot; height=&quot;1080&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangGraph 실행 과정을 살펴보면 AI 에이전트 Loop가 동작하는 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3794&quot; data-origin-height=&quot;2070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3qcZx/dJMcab47SNL/OiEeiKoqCGuO4KwKb50h90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3qcZx/dJMcab47SNL/OiEeiKoqCGuO4KwKb50h90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3qcZx/dJMcab47SNL/OiEeiKoqCGuO4KwKb50h90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3qcZx%2FdJMcab47SNL%2FOiEeiKoqCGuO4KwKb50h90%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;3794&quot; height=&quot;2070&quot; data-origin-width=&quot;3794&quot; data-origin-height=&quot;2070&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI, Unrolling the Codex Agent Loop:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://openai.com/index/unrolling-the-codex-agent-loop/&quot;&gt;https://openai.com/index/unrolling-the-codex-agent-loop/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;예제 저장소:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/langgraph-agent-loop&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/computer_science/ai/langgraph-agent-loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Ai</category>
      <category>langgraph</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/926</guid>
      <comments>https://malwareanalysis.tistory.com/926#entry926comment</comments>
      <pubDate>Sat, 2 May 2026 18:51:34 +0900</pubDate>
    </item>
    <item>
      <title>AI에게 youtube 영상을  내 관점으로 분석하게 시키는 방법</title>
      <link>https://malwareanalysis.tistory.com/925</link>
      <description>&lt;h1 id=&quot;요약&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;요약&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 AI로 YouTube 영상을 내가 원하는 방법으로 분석하는 방법을 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 방법 요약: Youtube &lt;span style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;자막 다운로드 한 후, AI 분석 청&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;주요-키워드&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;주요 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;YouTube Captions API&lt;/li&gt;
&lt;li&gt;yt-dlp&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ai로-youtube-영상을-분석하는-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AI로 Youtube 영상을 분석하는 방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI를 활용해 YouTube 영상을 내가 원하는 방향으로 분석하는 방법은 두 단계&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;YouTube 영상의 자막을 다운로드합니다.&lt;/li&gt;
&lt;li&gt;AI에게 원하는 관점으로 분석을 요청합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;자막-다운로드&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;자막 다운로드&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;YouTube 자막을 가져오는 방법은 두 가지로 나눌 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;YouTube Captions API를 직접 호출&lt;/li&gt;
&lt;li&gt;자막 다운로드 기능을 감싼 오픈소스 도구를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;YouTube Captions API 문서는 아래에서 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;YouTube Captions API: &lt;a href=&quot;https://developers.google.com/youtube/v3/docs/captions&quot;&gt;https://developers.google.com/youtube/v3/docs/captions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;직접 API를 호출하는게 부담스러우면, &lt;span&gt;&amp;nbsp;&lt;/span&gt;yt-dlp&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 오픈소스 도구를 사용하면 공개 영상의 자막을 더 쉽게 받을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yt-dlp GitHub 저장소: &lt;a href=&quot;https://github.com/yt-dlp/yt-dlp&quot;&gt;https://github.com/yt-dlp/yt-dlp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 id=&quot;ai에게-분석-요청하기&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AI에게 분석 요청하기&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 분석하는 방법은 말로 설명하면 됩니다. 저는 skills를 사용하여 제가 분석하고 싶은 내용을 말로 적었습니다. 저의 구현 예시는 아래 커밋에서 확인할 수 있습니다.&lt;br /&gt;- 저의 예시: &lt;a href=&quot;https://github.com/choisungwook/akbun-aitools/commit/d4eb529ff465ee112cea0bb3b666f28a27afff3d&quot;&gt;https://github.com/choisungwook/akbun-aitools/commit/d4eb529ff465ee112cea0bb3b666f28a27afff3d&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저의 Youtube 영상 분석 내용&lt;br /&gt;- 요약&lt;br /&gt;- 인사이트와 질문&lt;br /&gt;- 타임라인별 내용&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;2018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM8RQm/dJMcagSP5ya/8qZK9LOUKQnxaHyyeNuWvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM8RQm/dJMcagSP5ya/8qZK9LOUKQnxaHyyeNuWvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM8RQm/dJMcagSP5ya/8qZK9LOUKQnxaHyyeNuWvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM8RQm%2FdJMcagSP5ya%2F8qZK9LOUKQnxaHyyeNuWvk%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;1318&quot; height=&quot;2018&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;2018&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Ai</category>
      <category>YouTube</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/925</guid>
      <comments>https://malwareanalysis.tistory.com/925#entry925comment</comments>
      <pubDate>Sun, 26 Apr 2026 17:10:38 +0900</pubDate>
    </item>
    <item>
      <title>macOS끼리 SSH 연결이 자꾸 끊길 때 keepalive 설정하기</title>
      <link>https://malwareanalysis.tistory.com/924</link>
      <description>&lt;h1 id=&quot;요약&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;요약&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 글은 macOS 장비끼리 SSH로 접속할 때 세션이 간헐적으로 끊기는 문제를 다룹니다&lt;/b&gt;. 맥북프로에서 맥북미니로 원격 접속하던 중 5초 정도 입력이 없으면 연결이 끊기던 상황을 기준으로,&lt;b&gt; SSH keepalive 설정으로 세션을 안정화하는 방법을 정리&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;이-글에서-알-수-있는-것&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;이 글에서 알 수 있는 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;macOS 간 SSH 접속이 짧은 idle timeout 때문에 끊길 수 있는 이유&lt;/li&gt;
&lt;li&gt;ServerAliveInterval과&lt;span&gt;&amp;nbsp;&lt;/span&gt;ServerAliveCountMax로 SSH keepalive를 설정하는 방법&lt;/li&gt;
&lt;li&gt;클라이언트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;~/.ssh/config에 keepalive 설정을 추가하는 방법&lt;/li&gt;
&lt;li&gt;서버의&lt;span&gt;&amp;nbsp;&lt;/span&gt;/etc/ssh/sshd_config&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정 후 Remote Login을 재시작하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;주요-키워드&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;주요 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;macOS SSH 연결 끊김&lt;/li&gt;
&lt;li&gt;SSH idle timeout&lt;/li&gt;
&lt;li&gt;SSH keepalive&lt;/li&gt;
&lt;li&gt;ServerAliveInterval&lt;/li&gt;
&lt;li&gt;ServerAliveCountMax&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;배경&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;배경&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;맥북프로에서 맥북미니로 ssh 원격접속을 하고 있는데, 연결한 지 5초 마다 세션이 끊기고 다시 연결되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;원인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;원인&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원인은 ssh idle timeout이었습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ssh 세션에서 일정 시간 동안 패킷이 오가지 않으면 idle상태가 됩니다&lt;/b&gt;. 제 환경에서는 그 임계값이 굉장히 짧아서 5초만 입력이 없어도 끊겼습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 방법은 ssh가 주기적으로 keepalive 패킷을 보내게 만드는 것입니다&lt;/b&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;ServerAliveInterval로 패킷 전송 주기를 정하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ServerAliveCountMax로 응답이 없을 때 몇 번까지 재시도할지 정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;client-설정&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;client 설정&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 client쪽&lt;span&gt;&amp;nbsp;&lt;/span&gt;~/.ssh/config에 아래처럼 설정했습니다. 300초마다 keepalive 패킷을 보내고, 10번까지 응답이 없으면 그때 끊는다는 뜻입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777160689094&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ vi ~/.ssh/config
Host {hostname}
  HostName {ip 또는 hostname}
  ServerAliveInterval 300
  ServerAliveCountMax 10&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;server-설정&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;server 설정&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;server쪽도 동일하게 맞춰줘야 합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;/etc/ssh/sshd_config에 client와 같은 값으로 설정하고 ssh를 재시작했더니 그제서야 연결이 안정적으로 유지됐습니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ vi /etc/ssh/sshd_config
ServerAliveInterval 300
ServerAliveCountMax 10&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버에서는 sshd_config설정후 sshd를 재실행해야 합니다. 저는 맥 설정에서 sshd 를 off/on하셔 재실행했습니다.&lt;br /&gt;- System Settings &amp;rarr; General &amp;rarr; Sharing &amp;rarr; Remote Login&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1a1a1a;&quot; href=&quot;https://man.openbsd.org/ssh_config#ServerAliveInterval&quot;&gt;ssh_config man page - ServerAliveInterval&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Mac</category>
      <category>SSH</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/924</guid>
      <comments>https://malwareanalysis.tistory.com/924#entry924comment</comments>
      <pubDate>Sun, 26 Apr 2026 08:43:24 +0900</pubDate>
    </item>
    <item>
      <title>LM studio CLI(lms) 사용방법과 openclaw연동(예제 gemma4 MLX)</title>
      <link>https://malwareanalysis.tistory.com/923</link>
      <description>&lt;h1 id=&quot;lm-studio-clilms-사용-방법과-openclaw-연동-예제-gemma4-mlx&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;LM Studio CLI(lms) 사용 방법과 OpenClaw 연동 (예제: gemma4 MLX)&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;macOS에서 로컬 LLM을 실행할 때 많이 사용하는 선택지는 3가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ollama&lt;/li&gt;
&lt;li&gt;LM Studio와 CLI&lt;/li&gt;
&lt;li&gt;mlx&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 방법 중 저는 2번인 LM Studio CLI를 사용하여 gemma4를 실행했습니다. LM studio의 설명은 이전 글에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/794&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://malwareanalysis.tistory.com/794&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777203053258&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;LM studio 설치와 LLM 모델 다운로드&quot; data-og-description=&quot;1. 개요이 글은 LM studio 설치방법과 LM studio에서 허깅스페이스에 공개된 모델 설치하는 방법을 설명합니다.LM studio CLI(lms)는 아래 글에서 볼 수 있습니다.- https://malwareanalysis.tistory.com/923 LM studio CLI(&quot; data-og-host=&quot;malwareanalysis.tistory.com&quot; data-og-source-url=&quot;https://malwareanalysis.tistory.com/794&quot; data-og-url=&quot;https://malwareanalysis.tistory.com/794&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQlWL9/dJMb86n0oi8/1zm6WclVQES80DWshGrZZK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/s8F9t/dJMb81fVqDP/tJBsvio3djSdPFZ95fdpF1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/easydj/dJMb88F7JSj/jrKxzxZHqaHSg9phnkBSaK/img.png?width=3282&amp;amp;height=1530&amp;amp;face=0_0_3282_1530&quot;&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/794&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://malwareanalysis.tistory.com/794&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQlWL9/dJMb86n0oi8/1zm6WclVQES80DWshGrZZK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/s8F9t/dJMb81fVqDP/tJBsvio3djSdPFZ95fdpF1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/easydj/dJMb88F7JSj/jrKxzxZHqaHSg9phnkBSaK/img.png?width=3282&amp;amp;height=1530&amp;amp;face=0_0_3282_1530');&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;LM studio 설치와 LLM 모델 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 개요이 글은 LM studio 설치방법과 LM studio에서 허깅스페이스에 공개된 모델 설치하는 방법을 설명합니다.LM studio CLI(lms)는 아래 글에서 볼 수 있습니다.- https://malwareanalysis.tistory.com/923 LM studio CLI(&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;malwareanalysis.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;사양&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;사양&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mac mini 4 16GB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;lm-studio와-cli-설치&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;LM Studio와 CLI 설치&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공식 홈페이지의 설치 스크립트로 쉽게 설치할 수 있습니다. 설치 스크립트는 CLI만 설치하므로 LM Studio 데스크톱 앱은 별도로 설치해야 합니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;curl -fsSL https://lmstudio.ai/install.sh | bash&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;설치가 끝나면 lms 명령어로 CLI를 사용할 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;lms&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0QKB/dJMcadhuSFH/m38XrfhMenrLCHksuUiCg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0QKB/dJMcadhuSFH/m38XrfhMenrLCHksuUiCg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0QKB/dJMcadhuSFH/m38XrfhMenrLCHksuUiCg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0QKB%2FdJMcadhuSFH%2Fm38XrfhMenrLCHksuUiCg0%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;2052&quot; height=&quot;846&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;846&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;h1 id=&quot;lm-studio-런타임-확인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;LM Studio 런타임 확인&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LM Studio 런타임은 llama.cpp와 mlx 두 개를 지원합니다.&lt;/p&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;lms runtime ls&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTXbtI/dJMcadhuSFQ/8Di9q4A0fkzLzkDVKBsP2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTXbtI/dJMcadhuSFQ/8Di9q4A0fkzLzkDVKBsP2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTXbtI/dJMcadhuSFQ/8Di9q4A0fkzLzkDVKBsP2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTXbtI%2FdJMcadhuSFQ%2F8Di9q4A0fkzLzkDVKBsP2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2058&quot; height=&quot;234&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;234&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;h1 id=&quot;모델이-lms를-지원하는지-확인과-모델-다운로드-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;모델이 lms를 지원하는지 확인과 모델 다운로드 방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lms get 명령어로 모델을 검색하고 다운로드합니다. 따라서 lms get으로 MLX 모델 지원 여부도 확인할 수 있습니다. 만약 MLX 모델이 없다면 아래처럼 검색 에러가 발생합니다.&lt;/p&gt;
&lt;div id=&quot;cb4&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;lms get gemma-4 --mlx&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXkMXs/dJMcabqtmwR/EUk3B7GeRA515MbkjQP3lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXkMXs/dJMcabqtmwR/EUk3B7GeRA515MbkjQP3lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXkMXs/dJMcabqtmwR/EUk3B7GeRA515MbkjQP3lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXkMXs%2FdJMcabqtmwR%2FEUk3B7GeRA515MbkjQP3lK%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;1766&quot; height=&quot;236&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;236&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lms get은 기본적으로 LM Studio 카탈로그에서 모델을 가져옵니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://lmstudio.ai/models&quot;&gt;https://lmstudio.ai/models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4212&quot; data-origin-height=&quot;2166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N9FmX/dJMcacbOZmT/rIDPWqT1AuzK4hgkfSBkE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N9FmX/dJMcacbOZmT/rIDPWqT1AuzK4hgkfSBkE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N9FmX/dJMcacbOZmT/rIDPWqT1AuzK4hgkfSBkE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN9FmX%2FdJMcacbOZmT%2FrIDPWqT1AuzK4hgkfSBkE0%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;4212&quot; height=&quot;2166&quot; data-origin-width=&quot;4212&quot; data-origin-height=&quot;2166&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LM Studio 카탈로그에 MLX 모델이 없다면 Hugging Face에서 다운로드&lt;/b&gt;하면 됩니다. 아래 두 조건으로 모델을 검색합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;MLX&lt;/li&gt;
&lt;li&gt;LM Studio&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QPfMe/dJMcab421No/i9l68PhQyfqyIzp70pXsik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QPfMe/dJMcab421No/i9l68PhQyfqyIzp70pXsik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QPfMe/dJMcab421No/i9l68PhQyfqyIzp70pXsik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQPfMe%2FdJMcab421No%2Fi9l68PhQyfqyIzp70pXsik%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;3004&quot; height=&quot;1968&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1968&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Hugging Face에서 MLX 모델을 찾았다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://huggingface.co/&quot;&gt;https://huggingface.co/&lt;/a&gt;{repo}/{model}&lt;span&gt;&amp;nbsp;&lt;/span&gt;형식의 URL로 다운로드합니다.&lt;/p&gt;
&lt;div id=&quot;cb5&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;lms get https://huggingface.co/mlx-community/gemma-4-e4b-it-8bit&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2238&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHdnGk/dJMcaaLQ6SP/K365KrmiszlmxDiGIb8U6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHdnGk/dJMcaaLQ6SP/K365KrmiszlmxDiGIb8U6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHdnGk/dJMcaaLQ6SP/K365KrmiszlmxDiGIb8U6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHdnGk%2FdJMcaaLQ6SP%2FK365KrmiszlmxDiGIb8U6K%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;2238&quot; height=&quot;622&quot; data-origin-width=&quot;2238&quot; data-origin-height=&quot;622&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 lmstudio-community/gemma-4-E4B-it-MLX-4bit 모델을 다운로드했습니다.&lt;/p&gt;
&lt;div id=&quot;cb6&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;https://huggingface.co/lmstudio-community/gemma-4-E4B-it-MLX-4bit&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djOMcB/dJMcajvd7J5/28Usu3KjHx2RflYsBesOYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djOMcB/dJMcajvd7J5/28Usu3KjHx2RflYsBesOYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djOMcB/dJMcajvd7J5/28Usu3KjHx2RflYsBesOYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjOMcB%2FdJMcajvd7J5%2F28Usu3KjHx2RflYsBesOYK%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;2584&quot; height=&quot;492&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;492&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다운로드 도중 timeout이 발생하면 lms get을 다시 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3114&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdypLk/dJMcajaUvfy/mkdPzQHCWfAoYzoxkT3Wgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdypLk/dJMcajaUvfy/mkdPzQHCWfAoYzoxkT3Wgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdypLk/dJMcajaUvfy/mkdPzQHCWfAoYzoxkT3Wgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdypLk%2FdJMcajaUvfy%2FmkdPzQHCWfAoYzoxkT3Wgk%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;3114&quot; height=&quot;678&quot; data-origin-width=&quot;3114&quot; data-origin-height=&quot;678&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;h1 id=&quot;다운로드-받은-모델-조회&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;다운로드 받은 모델 조회&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lms ls 명령어로 다운로드한 모델을 조회할 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb7&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;lms ls&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BqmFs/dJMcag6mKaG/dDNSrCwq9WzCiODMILw2KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BqmFs/dJMcag6mKaG/dDNSrCwq9WzCiODMILw2KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BqmFs/dJMcag6mKaG/dDNSrCwq9WzCiODMILw2KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBqmFs%2FdJMcag6mKaG%2FdDNSrCwq9WzCiODMILw2KK%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;2268&quot; height=&quot;592&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;592&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;h1 id=&quot;모델-로드&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;모델 로드&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다운로드한 모델을 사용하려면 lms load 명령어로 모델을 메모리에 올려야 합니다. 먼저 lms ls로 모델 key를 조회한 후 lms load를 실행합니다.&lt;/p&gt;
&lt;div id=&quot;cb8&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;lms ls
lms load {model key} --gpu max --context-length 8192 --identifier gemma4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eo3u9V/dJMcadaFnRP/2vrKvUvjE2KBXzskvBSM01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eo3u9V/dJMcadaFnRP/2vrKvUvjE2KBXzskvBSM01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eo3u9V/dJMcadaFnRP/2vrKvUvjE2KBXzskvBSM01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feo3u9V%2FdJMcadaFnRP%2F2vrKvUvjE2KBXzskvBSM01%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;2608&quot; height=&quot;932&quot; data-origin-width=&quot;2608&quot; data-origin-height=&quot;932&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;h1 id=&quot;서버-실행&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;서버 실행&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로드한 모델을 사용하려면 LM Studio 서버를 실행합니다.&lt;/p&gt;
&lt;div id=&quot;cb9&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;lms server start --port 1234&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzeKL/dJMcacJGGqu/naM9m3rPnFaeIq7SMJMsSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzeKL/dJMcacJGGqu/naM9m3rPnFaeIq7SMJMsSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzeKL/dJMcacJGGqu/naM9m3rPnFaeIq7SMJMsSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtzeKL%2FdJMcacJGGqu%2FnaM9m3rPnFaeIq7SMJMsSk%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;1240&quot; height=&quot;128&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;128&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lms ps 명령어로 로드된 모델을 확인합니다.&lt;/p&gt;
&lt;div id=&quot;cb10&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;lms ps &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xDvO9/dJMcadhuSGc/wxZkKnjpOGd1tpqMu0fRc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xDvO9/dJMcadhuSGc/wxZkKnjpOGd1tpqMu0fRc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xDvO9/dJMcadhuSGc/wxZkKnjpOGd1tpqMu0fRc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxDvO9%2FdJMcadhuSGc%2FwxZkKnjpOGd1tpqMu0fRc1%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;2550&quot; height=&quot;256&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;256&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 LM Studio chat API에 identifier를 설정하여 질문을 보낼 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb11&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;curl http://localhost:1234/v1/chat/completions \
  -H &quot;Content-Type: application/json&quot; \
  -d '{
    &quot;model&quot;: &quot;gemma-4-e4b-it-mlx&quot;,
    &quot;messages&quot;: [
      {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;현재 서울 날씨는?&quot;}
    ],
    &quot;temperature&quot;: 0.3,
    &quot;max_tokens&quot;: 300
  }'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2846&quot; data-origin-height=&quot;1358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euhANk/dJMcagSPVSY/KMjQcykjZJNhkWTrgsAyy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euhANk/dJMcagSPVSY/KMjQcykjZJNhkWTrgsAyy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euhANk/dJMcagSPVSY/KMjQcykjZJNhkWTrgsAyy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuhANk%2FdJMcagSPVSY%2FKMjQcykjZJNhkWTrgsAyy0%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;2846&quot; height=&quot;1358&quot; data-origin-width=&quot;2846&quot; data-origin-height=&quot;1358&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;h1 id=&quot;종료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;종료&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lms server stop 명령어로 서버를 종료한 후, lms unload로 로드된 모델을 해제합니다.&lt;/p&gt;
&lt;div id=&quot;cb12&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;lms server stop  
lms unload gemma4 
또는 
lms unload --all&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;openclaw-연동&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;OpenClaw 연동&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw와 LM Studio를 연동하려면, LM Studio 모델의 context window 크기를 최소 16K 이상으로 설정해야 합니다.&lt;/p&gt;
&lt;div id=&quot;cb13&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;# 기존 모델 언로드
lms unload --all

# context length 지정해서 다시 로드
lms load gemma-4-e4b-it-mlx --context-length 32768 --gpu max&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OpenClaw와 LM Studio를 연동하려면 LM Studio 토큰도 필요합니다. 토큰은 LM Studio 데스크톱 앱에서만 설정&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;2298&quot; data-origin-height=&quot;1232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sEcic/dJMcabYirmp/DpFHba5CtnbbsBy8rijLEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sEcic/dJMcabYirmp/DpFHba5CtnbbsBy8rijLEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sEcic/dJMcabYirmp/DpFHba5CtnbbsBy8rijLEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsEcic%2FdJMcabYirmp%2FDpFHba5CtnbbsBy8rijLEk%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;2298&quot; height=&quot;1232&quot; data-origin-width=&quot;2298&quot; data-origin-height=&quot;1232&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw 설정이 익숙하지 않다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;openclaw onboard로 쉽게 설정할 수 있습니다. 설정 과정에서 LM Studio 토큰을 입력하는 단계가 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb14&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;openclaw onboard&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;1294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bau5of/dJMcagSPVS5/q8Otkt3lDjGk3roBCfwwx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bau5of/dJMcagSPVS5/q8Otkt3lDjGk3roBCfwwx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bau5of/dJMcagSPVS5/q8Otkt3lDjGk3roBCfwwx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbau5of%2FdJMcagSPVS5%2Fq8Otkt3lDjGk3roBCfwwx1%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;1328&quot; height=&quot;1294&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;1294&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw 설정이 모두 끝났다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;openclaw tui에서 LM Studio 모델을 사용할 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb15&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;openclaw tui&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2792&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXWoP6/dJMcagFj4lL/vywVh30BrDuoJYjjhkzXWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXWoP6/dJMcagFj4lL/vywVh30BrDuoJYjjhkzXWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXWoP6/dJMcagFj4lL/vywVh30BrDuoJYjjhkzXWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXWoP6%2FdJMcagFj4lL%2FvywVh30BrDuoJYjjhkzXWk%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;2792&quot; height=&quot;526&quot; data-origin-width=&quot;2792&quot; data-origin-height=&quot;526&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;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://lmstudio.ai/docs/cli/&quot;&gt;https://lmstudio.ai/docs/cli/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>lmstudio</category>
      <category>openclaw</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/923</guid>
      <comments>https://malwareanalysis.tistory.com/923#entry923comment</comments>
      <pubDate>Sun, 26 Apr 2026 08:13:31 +0900</pubDate>
    </item>
    <item>
      <title>site to site vpn 구축 후 꼭 체크해야할 것 - 재협상 정상유무</title>
      <link>https://malwareanalysis.tistory.com/922</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Site to Site VPN이 구축이 잘되고 통신이 잘되더라도, 꼭 확인해야할 항목이 있습니다.&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;Site to Site VPN의 터널은 한번 생성되면 계속 유지되는게 아니라, 주기적으로 재협상을 합니다. &lt;b&gt;만약 재협상이 실패하면 잘 실행되고 있는 VPN터널을 종료&lt;/b&gt;합니다. 그리고 다시 VPN협상을 시작해서 VPN터널을 생성합니다. 이 과정에서 VPN터널이 잠시 없기 때문에 순단이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- claude가 만든 영상 - Site to Site VPN 재협상 실패시 일어나는 일&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=t1s7nbgee60&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bAX9bB/dJMb9jgzM8G/X0TREarXKFIzjRHUovf5Sk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/b0jJgM/dJMb9kmfeMD/JR8G3vjvDiKnZugxzkOzRk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/kDyHC/dJMb82MFApQ/W8UxUTLEc41YbeZV2cCKvK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;악분 일상&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/t1s7nbgee60&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>VPN</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/922</guid>
      <comments>https://malwareanalysis.tistory.com/922#entry922comment</comments>
      <pubDate>Fri, 24 Apr 2026 00:06:20 +0900</pubDate>
    </item>
    <item>
      <title>회고  - Cilium으로 모든 egress  제한하는 방법 중 하나</title>
      <link>https://malwareanalysis.tistory.com/921</link>
      <description>&lt;h1 id=&quot;배경&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;배경&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아무 맥락 없는 처음 접해본 쿠버네티스 클러스터에 3~4일 정도 시간을 써서 개발환경 egress를 제한해야 하는 상황이었습니다. 해당 쿠버네티스는 국내 CSAP 인증을 받고 있었는데, 이 인증에서 egress 제한이 필요했습니다. 개발환경에서 잘 동작하면 운영팀이 스테이징과 운영환경에 적용할 예정이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시간도 부족했지만 가장 어려운 점은 클러스터 맥락을 모르는 것이었습니다. 거기에 cilium을 쓰고 있다는 것도 부담이었습니다. cilium을 잘 몰랐지만 hello world 튜토리얼을 공부한 기억이 있어서, cilium을 보자마자 CiliumNetworkPolicy로 egress를 차단하는 방향을 잡았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;요구사항-정리&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;요구사항 정리&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요구사항은 단 하나였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;허용된 곳만 egress가 가능해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;요구사항-펼치기&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;요구사항 구체화하기&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 요구사항을 구체화했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠버네티스 클러스터에 서로 다른 프로젝트가 있는가? &amp;rarr; 없다면 클러스터 내부통신을 전부 허용&lt;/li&gt;
&lt;li&gt;앞으로 다른 프로젝트가 이 클러스터에 들어오는가? &amp;rarr; 없다면 클러스터 내부통신을 전부 허용&lt;/li&gt;
&lt;li&gt;whitelist egress 방식은 어떻게 설정할까? &amp;rarr; 모든 namespace에 공통 규칙, 각 namespace에 필요한 것은 개별 규칙&lt;/li&gt;
&lt;li&gt;egress 접근 유형은 무엇인가? &amp;rarr; IPv4는 사용 IPv6는 없음, 도메인 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;실제-적용&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;실제 적용&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 프로젝트 1개만을 위한 전용 쿠버네티스 클러스터여서 클러스터 내부 통신을 허용했습니다. &lt;br /&gt;- 클러스터 공통규칙은&lt;span&gt;&amp;nbsp;&lt;/span&gt;CiliumClusterwideNetworkPolicy로 적용하고, namespace별 규칙은&lt;span&gt;&amp;nbsp;&lt;/span&gt;CiliumNetworkPolicy를 사용했습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;cilium은 entity를 제공하고 있어서 클러스터 내부 통신은 매우 쉽게 설정할 수 있었습니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-intra-cluster-egress
spec:
  endpointSelector: {}
  egress:
  - toEntities:
    - cluster
    - kube-apiserver&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;kube-dns가 외부 DNS 질의를 할 수 있도록 egress를 설정했습니다. 그리고 nodelocaldns도 사용하고 있어서 nodelocaldns도 동작하게 egress를 설정했습니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-external-dns
spec:
  endpointSelector:
    matchExpressions:
    - key: k8s:k8s-app
      operator: In
      values:
      - kube-dns
  egress:
  - toEntities:
    - world
    toPorts:
    - ports:
      - port: &quot;53&quot;
        protocol: UDP
      - port: &quot;53&quot;
        protocol: TCP
      rules:
        dns:
        - matchPattern: &quot;*&quot;
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-nodelocal-dns
spec:
  endpointSelector: {}
  egress:
  - toEntities:
    - host
    toPorts:
    - ports:
      - port: &quot;53&quot;
        protocol: UDP
      - port: &quot;53&quot;
        protocol: TCP
      rules:
        dns:
        - matchPattern: &quot;*&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;운영-고민-사항&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;운영 고민 사항&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;과거 차단된 로그를 어떻게 볼 것인가가 가장 큰 운영 이슈였습니다. cilium exporter를 사용하면 쉽게 해결되지만, 이 클러스터는 cilium 설정에 제약이 있었습니다. 어떤 CSP의 managed 쿠버네티스인데 cilium 설정을 변경하면 롤백하는 설정이 있다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 짧은 시간에 생각한 방법은&lt;span&gt;&amp;nbsp;&lt;/span&gt;hubble observe를 실행하는 deployment를 생성해서 차단 로그를 보는 것이었습니다. 아래는 수도코드입니다.&lt;/p&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;hubble status --server hubble-relay.kube-system.svc.cluster.local:80 || exit 1
while true; do
  hubble observe \
    --server hubble-relay.kube-system.svc.cluster.local:80 \
    --follow \
    --output jsonpb \
    --verdict DROPPED \
    --verdict AUDIT || true
  sleep 5
done&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>회고모음</category>
      <category>cilium</category>
      <category>kubernetes</category>
      <category>회고</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/921</guid>
      <comments>https://malwareanalysis.tistory.com/921#entry921comment</comments>
      <pubDate>Sun, 19 Apr 2026 23:27:10 +0900</pubDate>
    </item>
    <item>
      <title>kubernetes DNS캐시 - NodeLocalDns</title>
      <link>https://malwareanalysis.tistory.com/920</link>
      <description>&lt;h1 id=&quot;nodelocaldns&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;nodelocaldns&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;nodelocaldns는 kube-dns에 부담을 완해해주는 dns캐시역할입니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;쿠버네티스 pod개수가 증가할수록 kube-dns의 부담이 커지는데, kube-dns의 pod가 비정상상태가 되면 쿠버네티스 전체 장애로 확산됩니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 아키텍처 관점에서 DNS캐시가 필요했고, nodelocaldns가 쿠버네티스에서 DNS캐시 구현체입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1WFEW/dJMcagkTxQE/3glVdw64JzjTLYtIcuh5GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1WFEW/dJMcagkTxQE/3glVdw64JzjTLYtIcuh5GK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1WFEW/dJMcagkTxQE/3glVdw64JzjTLYtIcuh5GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1WFEW%2FdJMcagkTxQE%2F3glVdw64JzjTLYtIcuh5GK%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;2856&quot; height=&quot;1702&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1702&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;h1 id=&quot;localdns-설치방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;localdns 설치방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; 쿠버네티스 공식 홈페이지에 nodelocaldns설치 방법이 공개되어 있습니다. template.yaml에서 sed를 사용하여 localdns IP 등을 치환하여 설치합니다.&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/&quot;&gt;https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;설치 명령어는 아래가 끝입니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml

kubedns=$(kubectl get svc -n kube-system kube-dns -o jsonpath='{.spec.clusterIP}')
domain=cluster.local
localdns=169.254.20.10

# linux
sed -i &quot;s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g&quot; nodelocaldns.yaml


# mac
sed -i '' &quot;s|__PILLAR__LOCAL__DNS__|$localdns|g; s|__PILLAR__DNS__DOMAIN__|$domain|g; s|__PILLAR__DNS__SERVER__|$kubedns|g&quot; nodelocaldns.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;localdns는 hostnetwork를 사용하기 때문에, pod ip가 node ip와 동일합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3118&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chpTA6/dJMcaaSyaQE/eiUq1tQE8scGMDT6al1Lr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chpTA6/dJMcaaSyaQE/eiUq1tQE8scGMDT6al1Lr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chpTA6/dJMcaaSyaQE/eiUq1tQE8scGMDT6al1Lr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchpTA6%2FdJMcaaSyaQE%2FeiUq1tQE8scGMDT6al1Lr1%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;3118&quot; height=&quot;382&quot; data-origin-width=&quot;3118&quot; data-origin-height=&quot;382&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;h1 id=&quot;nodelocaldns-동작원리&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;nodelocaldns 동작 원리&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;nodelocaldns의 동작원리는 kube-dns로 가는 트래픽을 nodelocaldns pod가 대신 받게 하면 됩니다. &lt;b&gt;구현방법은 kube-proxy 사용 유무&lt;/b&gt;에 따라 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;kube-proxy를-쓰는-곳&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;kube-proxy를 쓰는 곳&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;kube-proxy를 쓰는 곳에서는 별도로 설정할 작업이 없습니다. nodelocaldns pod가 실행되면서 작업을 하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;noodelocaldns pod는  kube-dns pod ip로 가는  dns질의가 가지 않도록, 노드에 NOTRACK규칙을 추가합니다. NOTRACK은 iptables로 설정합니다. 그리고 local route를 생성하여 link local IP(169.254.20.10/32)를 갖는 nodelocaldns pod가 dns질의를 받도록합니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route show table local&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7zADN/dJMcaaE1rrp/6rS2CSVAzPFAjiWIF15cJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7zADN/dJMcaaE1rrp/6rS2CSVAzPFAjiWIF15cJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7zADN/dJMcaaE1rrp/6rS2CSVAzPFAjiWIF15cJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7zADN%2FdJMcaaE1rrp%2F6rS2CSVAzPFAjiWIF15cJ1%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;1512&quot; height=&quot;285&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqYrtL/dJMcacbJ3l1/18Yl57g0R1KPVykUYHmYA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqYrtL/dJMcacbJ3l1/18Yl57g0R1KPVykUYHmYA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqYrtL/dJMcacbJ3l1/18Yl57g0R1KPVykUYHmYA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqYrtL%2FdJMcacbJ3l1%2F18Yl57g0R1KPVykUYHmYA0%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;2032&quot; height=&quot;1158&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;kube-proxy를-안쓰는-곳&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;kube-proxy를 안쓰는 곳&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cilium처럼 kube-proxy를 안쓴다면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 pod의 /etc/resolv.conf nameserver를 nodelocaldns ip로 변경&lt;/b&gt;해야 합니다. ip는 link local ip인 169.254.20.10를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 /etc/resolv.conf 설정이 안되면 모든 pod에 DNS질의가 실패합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9rjkc/dJMcahD50WJ/ODugjNHOxJpyKZqoFkRDPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9rjkc/dJMcahD50WJ/ODugjNHOxJpyKZqoFkRDPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9rjkc/dJMcahD50WJ/ODugjNHOxJpyKZqoFkRDPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9rjkc%2FdJMcahD50WJ%2FODugjNHOxJpyKZqoFkRDPk%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;1824&quot; height=&quot;346&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;346&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;h1 id=&quot;실습&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;실습&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희 github에 실습자료가 있습니다.&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;github 주소: &lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/kubernetes/localdns&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/kubernetes/localdns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>kubernetes</category>
      <category>nodelocaldns</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/920</guid>
      <comments>https://malwareanalysis.tistory.com/920#entry920comment</comments>
      <pubDate>Sun, 19 Apr 2026 20:58:32 +0900</pubDate>
    </item>
    <item>
      <title>opus 4.7 출시와 내가 생각하는 큰 변화 2가지 - 비용증가와  이미지  처리 성능 증가</title>
      <link>https://malwareanalysis.tistory.com/919</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;opus 4.7이 2026.4.17에 공개되었습니다.&lt;br /&gt;- 출시 뉴스:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.anthropic.com/news/claude-opus-4-7&quot;&gt;https://www.anthropic.com/news/claude-opus-4-7&lt;/a&gt;&lt;br /&gt;-  새로운 기능:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://platform.claude.com/docs/ko/about-claude/models/whats-new-claude-4-7&quot;&gt;https://platform.claude.com/docs/ko/about-claude/models/whats-new-claude-4-7&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;사용-비용은-증가함&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;사용 비용은 증가함&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;opus 4.7 토큰당 비용은 4.6과 동일합니다. &lt;b&gt;하지만, opus 4.7을 사용할 때 input/output 토큰수는 최대 1.35 더 사용&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;2592&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LVFi4/dJMcaaLLdXG/cnriqhhylpijOqRfb4ScZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LVFi4/dJMcaaLLdXG/cnriqhhylpijOqRfb4ScZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LVFi4/dJMcaaLLdXG/cnriqhhylpijOqRfb4ScZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLVFi4%2FdJMcaaLLdXG%2FcnriqhhylpijOqRfb4ScZ1%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;2592&quot; height=&quot;736&quot; data-origin-width=&quot;2592&quot; data-origin-height=&quot;736&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;h1 id=&quot;이미지-처리-성능-증가&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;이미지 처리 성능 증가&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미지 처리 성능이 증가하여 이미지를 더 깊게 이해하고 정확도가 상승되었습니다. 저는 기존에 제가 작성했던 powerpoint 를 이해하고 최대한 저의 스타일로 비슷하게 재현할 수 있을지 기대됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Claude</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/919</guid>
      <comments>https://malwareanalysis.tistory.com/919#entry919comment</comments>
      <pubDate>Fri, 17 Apr 2026 21:56:50 +0900</pubDate>
    </item>
    <item>
      <title>파인튜닝이란? - 맞춤형 AI모델</title>
      <link>https://malwareanalysis.tistory.com/918</link>
      <description>&lt;h1 id=&quot;파인튜닝이란&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;파인튜닝이란&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파인튜닝(fine tuning)은 이미 만들어진 AI 모델을 재학습시켜 특정 작업에 특화되도록 만드는 과정입니다&lt;/b&gt;. 특정 상황에 맞게 조정된 모델이기 때문에 맞춤형 AI 모델이라고 부를 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2598&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PTa4l/dJMcadhl9pv/MrfzkYAV04M6pucRG3SNq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PTa4l/dJMcadhl9pv/MrfzkYAV04M6pucRG3SNq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PTa4l/dJMcadhl9pv/MrfzkYAV04M6pucRG3SNq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTa4l%2FdJMcadhl9pv%2FMrfzkYAV04M6pucRG3SNq1%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;2598&quot; height=&quot;724&quot; data-origin-width=&quot;2598&quot; data-origin-height=&quot;724&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;h1 id=&quot;파인튜닝-예제&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;파인튜닝 예제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습은 openAI파인튜닝으로 진행했습니다. training데이터가 충분하지 않아서 파인튜닝이 잘 안되었습니다. ㅜㅜ. 실습코드와 실습방법은 저의 github에 공개되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실습코드: &lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/fine-tuning&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/ai/fine-tuning&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저는 프롬프트에&lt;span&gt;&amp;nbsp;&lt;/span&gt;resources.requests와&lt;span&gt;&amp;nbsp;&lt;/span&gt;resources.limits를 따로 지시하지 않아도 모델이 항상 resources를 설정해 주기를 원했습니다(프롬프트로 대체할 수 있는 영역이지만...).&lt;/b&gt; 그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;gpt-4.1-nano&lt;span&gt;&amp;nbsp;&lt;/span&gt;모델을 파인튜닝하기로 했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 resources가 포함된 deployment manifest로 &lt;span style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;training&lt;/span&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;1152&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLiYOH/dJMcab4UqIv/R7VRr9QWYBzYOfpgDGFKE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLiYOH/dJMcab4UqIv/R7VRr9QWYBzYOfpgDGFKE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLiYOH/dJMcab4UqIv/R7VRr9QWYBzYOfpgDGFKE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLiYOH%2FdJMcab4UqIv%2FR7VRr9QWYBzYOfpgDGFKE1%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;1152&quot; height=&quot;480&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;480&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;준비한 training 데이터셋으로 OpenAI 파인튜닝 작업을 생성했습니다. &lt;/span&gt;파인튜닝 작업을 제출한 뒤에는 아래처럼 job ID로 진행 상태를 조회할 수 있습니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;job = client.fine_tuning.jobs.create(
  training_file=file_response.id,
  model=&quot;gpt-4.1-nano-2025-04-14&quot;,
  suffix=&quot;k8s-deploy&quot;,
  hyperparameters={&quot;n_epochs&quot;: 1},
)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파인튜닝 진행 과정은 OpenAI 플랫폼에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3696&quot; data-origin-height=&quot;2270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pr5c1/dJMcaiQs0DR/9aixsaKdtA8lBWxFYjHu60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pr5c1/dJMcaiQs0DR/9aixsaKdtA8lBWxFYjHu60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pr5c1/dJMcaiQs0DR/9aixsaKdtA8lBWxFYjHu60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPr5c1%2FdJMcaiQs0DR%2F9aixsaKdtA8lBWxFYjHu60%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;3696&quot; height=&quot;2270&quot; data-origin-width=&quot;3696&quot; data-origin-height=&quot;2270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>Ai</category>
      <category>스터디</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/918</guid>
      <comments>https://malwareanalysis.tistory.com/918#entry918comment</comments>
      <pubDate>Wed, 15 Apr 2026 00:36:52 +0900</pubDate>
    </item>
    <item>
      <title>[책 리뷰] 일 잘하는 엔지니어의 생각 기법</title>
      <link>https://malwareanalysis.tistory.com/917</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;팀원의 추천으로 &quot;일 잘하는 엔지니어의 생각 기법&quot;을 읽게 되었습니다.&amp;nbsp;&lt;br&gt;&lt;b&gt;이 책은 기술 서적이 아니고 저자가 수십 년 동안 현장에서 문제를 해결하며 쌓은 사례와 사고방식을 공유&lt;/b&gt;하는 책입니다. 2025년 해외에서 출간되었고 번역본으로 읽을 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;이 책은 개인적으로 경험이 적은 엔지니어에게는 추천하기 어렵습니다&lt;/b&gt;. 어느 정도 일에 익숙해지고 현장에서 몇 번의 갈등을 겪어본 사람이 읽으면 좋을 것 같습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이 책에는 저자가 직접 겪은 여러 교훈과 사례가 담겨 있습니다. 그중 몇 가지를 소개합니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;1. 기술 종사자들은 대체로 자신의 고객을 방문하지 않으려 한다&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 회사에서 풀지 못한 문제를 들여다보다가, 담당자들이 전화나 FAX로만 응대하고 있었다는 사실을 깨닫습니다. 장거리였지만 직접 고객사를 방문했고, 현장을 보자마자 문제를 간단히 해결합니다.&lt;br&gt;문제가 잘 해결 안될 때는 직접 현장에 가서 확인하면 좋다는 교훈이 줍니다. 저도 1~2주전에 채팅으로 잘 해결이 안되어 직접 대면으로 이야기나눠 5분안에 해결한 적이 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;2. 증상을 나열하고 비즈니스 우선순위에 따라 하나씩 해결한다&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 생기면 보통 한 가지가 아니라 여러 증상이 동시에 나타납니다. 이 사례에서 저자의 회사는 고소를 당할 만큼 오랫동안 해결하지 못한 문제들을 안고 있었습니다. 저자는 긴급히 파견을 가서 증상을 모두 나열하고, 비즈니스 영향도에 따라 순서를 매긴 뒤 하나씩 해결해 나갑니다. 나열하지 않았다면 어떤 증상이 어떤 리스크로 이어지는지 파악할 수 없었고, 결국 고객의 고소를 막지 못했을 것입니다.&lt;br&gt;당연한 말 같지만 실제 우선순위를 나열하고 판단하는 것은 쉽지 않습니다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;3. 부하와 응답 시간은 선형 관계가 아니다&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;부하가 큰 시스템에 부하를 더하면 응답 시간은 급격히 증가합니다. 반대로 무거운 작업의 부하를 조금만 줄여도 응답 시간이 크게 개선됩니다. 이런 관계를 비선형이라고 하며, 어느 지점에서 개선 효과가 나타날지 예측하기 어렵습니다.&lt;br&gt;부하에 따른 응답 시간을 계산할 때 사람들은 보통 선형 그래프를 떠올립니다. 하지만 저자는 부하의 실제 양상은 선형이 아니라고 말합니다. 부하가 커질수록 응답 시간은 기하급수적으로 늘어나는 곡선 형태가 됩니다. 그래서 부하가 큰 지점을 조금만 줄여도 응답 시간이 크게 낮아지는 효과를 얻을 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;4. 답할 수 없는 질문 앞에서는 대체 질문에 만족하는 경향이 있다&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 문제를 해결하는 과정에서 다양한 유형의 사람을 만납니다. 그중에는 자신이 답할 수 없는 문제를 마주했을 때, 진짜 해답이 아니더라도 뭔가 해결될 것처럼 보이는 방향으로 기우는 사람이 많다고 합니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;5. 전체 최적화보다 우선순위가 높은 병목 하나에 집중한다&lt;/h1&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 전체를 최적화하고 싶은 마음은 이해하지만, 가장 좋은 방법은 증상을 한 번에 하나씩 처리하는 것입니다. 전체를 동시에 다루면 엉뚱한 방향으로 벗어나기 쉽습니다.&lt;br&gt;따라서 비즈니스 우선순위가 가장 높은 증상의 병목에 집중해야 합니다. 2번 사례와 마찬가지로, 우선순위가 높은 병목 하나에 집중하면 전체 시스템의 많은 부분이 함께 개선됩니다.&lt;br&gt;&lt;br&gt;—————-&lt;br&gt;스크랩: &lt;br&gt;p48 기술 종사자들은 대체로 자신의 고객을 방문하지 않으려 한다&lt;br&gt;p49 그 문제를 내가 이해해야 한다는 것이다. 그래야 나가 도움을 주거나 받을 수 있다&lt;br&gt;p49 문제의 증상을 사용자가 바라보는 방식으로 관찰해야한다&lt;br&gt;p67 증상의 목록을 나열하고 비지니스 우선순위에 따라 나열한 후 증상을 관찰하고 지연이 발생하는 원인을 찾아 조치한다&lt;br&gt;p71 원인이 한가지라고 생각하지 말자&lt;br&gt;p74 가장 비용이 적게 드는 방법은 실패해도 타격이 크지 않는다&lt;br&gt;p102 재현 가능한 테스트 케이스를 제공한다. 어떻개 해야 다른 사람이 이 문제를 재현할 수 있을까? 기대했던 동작은 무엇인가? 실제로 발생항 동작은 무엇인가?&lt;br&gt;p105 workload -&amp;gt; 작업부하&lt;br&gt;p108 프로그램을 재시작하지 않고도 추적을 사용할수 있게 하는 기능은 매우 바람직하다&lt;br&gt;p111 개발자들이 사용할 도구를 제공하는 것만으로는 충분하지 않다. 개발자들 역시 스스로 도구를 사용할 의지와 기술을 갖추어야 한다&lt;br&gt;p119 프로그램의 속도를 느리게 만든 적이 있는 몇가지 이벤트 종류만 이해하면 된다.&lt;br&gt;p119 프로파일은 프로그램이 시간을 어떻게 사용하는지를 보여준다. 또한 어떻게 시간을 하지하는지도 보여준다&lt;br&gt;p132 데이터베이스에 인덱스가 존재하는 이유도 필터링을 일찍 수행하기 위해서이다&lt;br&gt;p141 전체 시스템을 최적화하고 싶어하는건 이해하지만 가장 좋은 방법은 한 번에 증상을 하나씩 처리하는 것이다. 전체를 하면 엉뚱한 방향으로 벗어날 수 있다. 따라서 비지니스에서 가장 우선순위가 높은 증상의 병목에 집중해야 한다&lt;br&gt;p144 여러 항목의 연관관계를 파악하는 것이다. 그러러면 프로파일상의 각 이벤트의 의미를 해해야 한다&lt;br&gt;p155 여러분이 분석하고자 하는 프로그램 기능이 정상적일때는 어떻게 동작하는지 이해를 할 수 있다면 믄제 해결을 쉬워진다&lt;br&gt;p160 어떤 리소스 사용량이 많아지면 그 리소스를 획득하기까지 기다리는 시간이 많아진다&lt;br&gt;p205 비즈니스에 중요한 증상을 가장 빠르게 완화할수 있는 다음 4가지 질문에 답할 수 있다&lt;br&gt;1.&amp;nbsp;&amp;nbsp;얼마나 걸리나&lt;br&gt;2. 왜&lt;br&gt;3. 만일 한다면&lt;br&gt;4. 그 밖에&lt;br&gt;p206 자신이 답할 수 없는 질문을 맞닥뜨릴 경우 대체질문에 만족하는 경향을 보인다&lt;br&gt;p210 문제해결과정에서 진전은 현재 일어나고 있는 일에 더 많은 것을 알게 될때 가능해진다&lt;br&gt;p243 이벤트 간의 상호의존성을 이해하면 예측의 정확도를 높읠 수 있다&lt;br&gt;p246 부하가 큰 시스템에 부하를 더하면 응답시간이 급증하게 된다. 반대로 무거운 작업 무하를 줄일 수 있다면 응답시간이 엄청나게&lt;br&gt;개선된다. 이런 것을 비선형이라고 하며 비선형적으로 개선괴는 부분을 예측하기 어렵다&lt;br&gt;p257 병렬작업으로 응답시간은 줄일 수 있지만 부하가 줄어들지 않는다&lt;br&gt;p290 비율(처리량, latency 등)을 확인할때는 의구심을 들어야한다. latency를 줄인다고 하면 처리량이 빠른 task를 많이 실행하면된다.&lt;br&gt;p303 테스트를 하려면 1)실질적인 데이터와 2) 실제 트래픽 강도 이 2가지 없이는 발견하기 어려운 성능 문제도 많다.&lt;br&gt;p309 10개월 전 작성한 프로그램을 수정하는 것은 어제 작성한 프로그램을 수정하는 것보다 훨씬 많은 시간이 걸린다. 그 시기의 배경지식을 다시 떠올리는 시간도 필요하며 지난 10개월간 새로 나타난 상호의존성을 처리하는 것은 그보다 많은 시간이 걸릴 수 있다.&lt;br&gt;p310 개발자와 관리자는 완전히 겁을 먹는 정도는 아니더라도 코드 변경을 꺼리기도 한다. 만에 하나 다음 테스트 시점에 어떤 부수적 피해가 발견될지 몰라 두려움에 시달리는 것이다. &lt;br&gt;p320 용량계획(capacity planning)&lt;br&gt;p322 최적화는 둘 중 하나로 진행해야 한다. 1. 이벤트 카운트를 줄이거나 2. 각 이벤트의 평균 실행 시간을 줄이면 된다.&lt;br&gt;p334 프로젝트의 위험을 확인하는 7가지 방법&lt;br&gt;1. 잠재 고객객의 목표: 결과물일까? 안도감일까?&lt;br&gt;2. 잠개 고객의 성향: 배타적인가? 수용적인가?&lt;br&gt;3. 잠재 고객의 문화: 정치적인가? 기술적인가?&lt;br&gt;4. 잠재 고객의 분위기: 공황 상태인가? 침착한 상태인가?&lt;br&gt;5. 잠개 고객의 주의력: 방치하는가? 배려하는가?&lt;br&gt;6. 잠재 고객의 자기 인식: 환상인가? 실제인가??&lt;br&gt;7. 권한에 대한 접근: 불가능한가? 가능한가?&lt;br&gt;p344 여러분이 뭔가를 고친다는 것은 곧 다른 누간가가 일을 망쳤다는 암시를 만들어 낼 수 있음을 꺠달아햐 한다.&lt;br&gt;p351 단순히 제안서를 작성하는 것보다는, 기업의 비지니스 문제를 해결하기 위해 지속적으로 노력할 사람에게 영감을 주는 편이 훨씬 나은 방법이다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;</description>
      <category>기타영역 공부 기록</category>
      <category>책</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/917</guid>
      <comments>https://malwareanalysis.tistory.com/917#entry917comment</comments>
      <pubDate>Sun, 12 Apr 2026 22:38:32 +0900</pubDate>
    </item>
    <item>
      <title>Java (pod)가 부팅 직후 느린 이유: Lazy Loading과 JVM warm up</title>
      <link>https://malwareanalysis.tistory.com/916</link>
      <description>&lt;h1 id=&quot;jvm-warm-up이란&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;JVM warm up이란?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Java 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;.class&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일(bytecode)로 컴파일되고, JVM이 이 bytecode를 로딩해서 실행합니다. 이때 Lazy Loading이라는 개념이 등장합니다. class가 처음 호출되기 전까지 로딩을 미루는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Lazy Loading 때문에 Java pod가 부팅된 직후에는 아직 로딩되지 않은 class가 많습니다. 그래서 부팅 시점에 미리 class를 로딩해 둘지 결정하는 옵션이 있고, 이 과정을 JVM warm up이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;warm-up이-서비스-성능에-어떻게-영향을-미치는가&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;warm up이 서비스 성능에 어떻게 영향을 미치는가?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; 애플리케이션 덩치가 커질수록 &lt;b&gt;warm up 적용 여부가, 부팅이 막 된 서비스의 성능에 큰 영향을 끼칩니다.&lt;/b&gt; 아래 사례 에서는 배포 직후 DB 커넥션 풀 에러를 JVM warm up으로 해결한 사례를 다룹니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/jvm-warm-up&quot;&gt;https://tech.kakaopay.com/post/jvm-warm-up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;class-lazy-loading-실습&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;class Lazy Loading 실습&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;class Lazy Loading을 직접 확인하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;class가 로딩될 때마다 로그를 출력&lt;/b&gt;하게 설정하면 됩니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;environment:
  JAVA_OPTS: &quot;-Xlog:class+load=info&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; 로그는 로딩된 시간과 어떤 class가 로딩되었는지 보여줍니다. 로그의 가장 왼쪽 타임스탬프가 로딩된 시간입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775994089696&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[0.123s][info][class,load] com.example.classloading.service.ProductQueryService source: file:/app/app.jar
[0.456s][info][class,load] org.springframework.data.jpa.repository.JpaRepository source: file:/app/app.jar&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot는 애플리케이션이 실행될 때 component scan을 통해 service, controller 등 bean으로 등록되는 class를 미리 로딩합니다. 그래서 Spring Boot를 실행하면 class 로딩 로그에서 Spring Boot 컴포넌트를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2914&quot; data-origin-height=&quot;1100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cev85l/dJMcajoeXIj/qW9iGFfvRGnvBOEfktnbK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cev85l/dJMcajoeXIj/qW9iGFfvRGnvBOEfktnbK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cev85l/dJMcajoeXIj/qW9iGFfvRGnvBOEfktnbK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcev85l%2FdJMcajoeXIj%2FqW9iGFfvRGnvBOEfktnbK0%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;2914&quot; height=&quot;1100&quot; data-origin-width=&quot;2914&quot; data-origin-height=&quot;1100&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제가 만든 예제로 테스트해 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;특정 API를 호출한 전후로 로딩된 클래스 개수가 증가&lt;/b&gt;하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/computer_science/jvm_class_loading&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/computer_science/jvm_class_loading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;API 호출 전에는 로딩된 클래스가 14,410개였는데, API를 호출한 뒤에는 14,772개로 증가했습니다. 그 이후로는 같은 API를 반복 호출해도 14,772개에서 변화가 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2090&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNzjg/dJMcafTLsHS/tj2MvJTJUALDLPQSIVpdwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNzjg/dJMcafTLsHS/tj2MvJTJUALDLPQSIVpdwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNzjg/dJMcafTLsHS/tj2MvJTJUALDLPQSIVpdwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNzjg%2FdJMcafTLsHS%2Ftj2MvJTJUALDLPQSIVpdwk%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;2090&quot; height=&quot;1072&quot; data-origin-width=&quot;2090&quot; data-origin-height=&quot;1072&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;h1 id=&quot;jvm-warm-up-실습&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;JVM warm up 실습&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;warm up 효과를 코드로 직접 보여주기는 어려웠지만, JVM warm up이 배포 직후 애플리케이션 성능에 실제로 영향을 준다는 것은 체감할 수 있게 했습니다.&lt;br /&gt;- 예제 코드:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/computer_science/jvm_warmup&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/computer_science/jvm_warmup&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 API를 대상으로 warm up 적용 전후의 latency를 비교했습니다. k6로 부하를 발생시키고 k6 보고서에서 latency를 확인했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;p90, p99 기준으로 warm up을 수행한 애플리케이션이 부팅 직후 warm up을 하지 않은 경우보다 latency가 16% 빨랐습니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;시간이 지나면 두 경우의 latency는 비슷해졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLpWgY/dJMcajaKbAu/FCjg5jWivIiETrUSkNy3F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLpWgY/dJMcajaKbAu/FCjg5jWivIiETrUSkNy3F0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLpWgY/dJMcajaKbAu/FCjg5jWivIiETrUSkNy3F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLpWgY%2FdJMcajaKbAu%2FFCjg5jWivIiETrUSkNy3F0%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;2436&quot; height=&quot;858&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2656&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNnIDJ/dJMcaaEWboX/J4bwLdU3eU3dQzKTaMjBg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNnIDJ/dJMcaaEWboX/J4bwLdU3eU3dQzKTaMjBg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNnIDJ/dJMcaaEWboX/J4bwLdU3eU3dQzKTaMjBg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNnIDJ%2FdJMcaaEWboX%2FJ4bwLdU3eU3dQzKTaMjBg1%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;2656&quot; height=&quot;1082&quot; data-origin-width=&quot;2656&quot; data-origin-height=&quot;1082&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;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/jvm-warm-up&quot;&gt;https://tech.kakaopay.com/post/jvm-warm-up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>java</category>
      <category>kubernetes</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/916</guid>
      <comments>https://malwareanalysis.tistory.com/916#entry916comment</comments>
      <pubDate>Sun, 12 Apr 2026 20:39:54 +0900</pubDate>
    </item>
    <item>
      <title>claude code로 wordpress 테마를 변경하고 페이지 렌더링 속도를 98% 단축한 후기</title>
      <link>https://malwareanalysis.tistory.com/915</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리 스킨에 이어서 워드프레스 테마도 Claude Code로 변경했습니다. 테마를 변경하면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;페이지 로딩 속도도 98% 단축&lt;/b&gt;했습니다. 평균 페이지 로딩 시간이 6초에서 107ms로 줄었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Claude Code로 티스토리 변경한 후기: &lt;a href=&quot;https://malwareanalysis.tistory.com/914&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://malwareanalysis.tistory.com/914&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;사용하는-서버&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;사용하는 서버&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cafe24 WordPress 호스팅 상품을 약 3년째 사용 중입니다. 월 1,100원으로 정말 저렴해서 계속 사용하고 있습니다. 서버 사양은 CPU 1core, 메모리 1GB 정도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;고민했던-것&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;고민했던 것&lt;/h1&gt;
&lt;h2 id=&quot;claude-code가-어떤-기준으로-wordpres테마를-만들게-할까&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;claude code가 어떤 기준으로 wordpres테마를 만들게 할까?&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저의 &lt;b&gt;기준은 4개&lt;/b&gt;였습니다.&lt;br /&gt;1. 페이지 랜딩속도가 150ms이여야한다.&lt;br /&gt;2. PC, 모바일 모두 지원되는 반응형이여야 한다.&lt;br /&gt;3. 구글 애드센스의 영향을 받으면 안된다.&lt;br /&gt;4. 디자인은 글목록, 메뉴만  보여준다. (디자인 챕터에 설명)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 기준을 잡고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;claude code ultraplan으로 작업을 시작&lt;/b&gt;했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;디자인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;디자인&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 단순한 것을 좋아해서 메인 페이지는 글 제목만 나열되도록 만들었습니다. 메뉴도 꼭 필요한 것만 배치했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;2422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RKujG/dJMcab4RTeY/jCRl8V1Ez0kMsuuNQZHoi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RKujG/dJMcab4RTeY/jCRl8V1Ez0kMsuuNQZHoi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RKujG/dJMcab4RTeY/jCRl8V1Ez0kMsuuNQZHoi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRKujG%2FdJMcab4RTeY%2FjCRl8V1Ez0kMsuuNQZHoi1%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;2252&quot; height=&quot;2422&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;2422&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;h1 id=&quot;페이지-로딩-속도는-왜-개선되었는가&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;페이지 로딩 속도는 왜 개선되었는가?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 페이지 로딩 속도가 느린 원인은 유료 테마&lt;/b&gt;에 있었습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 WordPress를 시작할 때 아무것도 몰라서 유명한 유료 테마를 사는 게 좋을 것 같았습니다. 그래서 Newspaper 테마를 약 8만원에 구매해서 사용했습니다. Newspaper 테마는 빌더반이여서 코딩 없이 PHP 레이아웃을 만들 수 있는 장점이 있습니다. 하지만 Newspaper가 정한 규칙에 따라 페이지가 렌더링되어 필요 없는 기능도 함께 실행됩니다. 필요 없는 부분만 제거할 수 없어서 모든 페이지 로딩 속도가 느려졌습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;페이지 로딩속도를 느린부분을 찾았지만, 해당 부분을 제거하거 수정하면 페이지 렌더링 오류가 발생했습니다. 분석시간이 많이 걸릴 것 같아 나중에 해야지 생각만 하다가 2년넘게 방치되었습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시간이 지나 WordPress 페이지 로딩 속도가 느린 것이 기억나, Claude Code로 빌더기반이 아닌 순수 php기반으로 실행하게 하여 페이지 로딩속도를 개선했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/915</guid>
      <comments>https://malwareanalysis.tistory.com/915#entry915comment</comments>
      <pubDate>Fri, 10 Apr 2026 19:05:44 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code + Codex로 티스토리 스킨 제작 후기</title>
      <link>https://malwareanalysis.tistory.com/914</link>
      <description>&lt;p&gt;claude code, codex를 사용해서 티스토리 스킨을 만들었습니다. 총 6시간정도 걸렸는데 걸린시간에 비해 만족스럽습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;티스토리 스킨 코드: &lt;a href=&quot;https://github.com/choisungwook/portfolio/tree/master/product/tistory-skin/akbun&quot;&gt;https://github.com/choisungwook/portfolio/tree/master/product/tistory-skin/akbun&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;claude code와 codex두개를 쓴 이유는, 로컬에서 잘 실행되었던 스킨을 티스토리에 적용하니 오류가 나서 서로 컨텍스트를 교환하면서 수정하게 했습니다. &lt;/p&gt;
&lt;p&gt;재밌었던 점은 &lt;strong&gt;cmux를 사용함으로써 playwright mcp 직접 사용 안했습니다&lt;/strong&gt;. cmux처럼 웹브라우저가 내장된 터미널이 유행일 수 있겠다 생각했습니다.&lt;/p&gt;
&lt;h1&gt;UI&lt;/h1&gt;
&lt;p&gt;homepage의 주요 패널은 4개입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;왼쪽 레이아웃: 글 검색, 카테고리, 태그&lt;/li&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;3316&quot; data-origin-height=&quot;2448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iy0s1/dJMcajuYANJ/NxSOKYhok2JDjessrMk6J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iy0s1/dJMcajuYANJ/NxSOKYhok2JDjessrMk6J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iy0s1/dJMcajuYANJ/NxSOKYhok2JDjessrMk6J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIy0s1%2FdJMcajuYANJ%2FNxSOKYhok2JDjessrMk6J0%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;3316&quot; height=&quot;2448&quot; data-origin-width=&quot;3316&quot; data-origin-height=&quot;2448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;글의 내용은 크게 5가지입니다. 글의 내용은 3개를 신경썼습니다.&lt;br&gt;1. 글 내용을 보여주는 가로길이&lt;br&gt;2. 오른쪽 목차를 보여주는 ToC&lt;br&gt;3. 헤더 H1의 글씨색깔을 다크레드로 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3898&quot; data-origin-height=&quot;2298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtM3J1/dJMcaipkQhB/VhtA8OWncE7eKCuBKBG1L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtM3J1/dJMcaipkQhB/VhtA8OWncE7eKCuBKBG1L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtM3J1/dJMcaipkQhB/VhtA8OWncE7eKCuBKBG1L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtM3J1%2FdJMcaipkQhB%2FVhtA8OWncE7eKCuBKBG1L1%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;3898&quot; height=&quot;2298&quot; data-origin-width=&quot;3898&quot; data-origin-height=&quot;2298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;어려웠던 점&lt;/h1&gt;
&lt;h2&gt;티스토리 스킨 개발 가이드&lt;/h2&gt;
&lt;p&gt;티스토리 스킨 가이드는 공개되어 있습니다. 기준도 명확하고 내용이 길지 않습니다. 하지만, AI agent가 가이드를 보고 많이 헤맸습니다. 그래서 저는 UI에 원하는 부분만 작업할 수 있도록 해당 내용만 마크다운으로 추출했습니다. 이 과정에서 토큰소모를 엄청했습니다.&lt;br&gt;- &lt;a href=&quot;https://tistory.github.io/document-tistory-skin/&quot;&gt;https://tistory.github.io/document-tistory-skin/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;배포&lt;/h2&gt;
&lt;p&gt;티스토리 배포는 관리자페이지에서 파일 한개 한개 업로드하는 방법이어서 자동화를 못했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2852&quot; data-origin-height=&quot;1550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB5wE6/dJMb99MEHRA/woW7Mh4RXguuRR6yKP0TO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB5wE6/dJMb99MEHRA/woW7Mh4RXguuRR6yKP0TO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB5wE6/dJMb99MEHRA/woW7Mh4RXguuRR6yKP0TO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB5wE6%2FdJMb99MEHRA%2FwoW7Mh4RXguuRR6yKP0TO1%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;2852&quot; height=&quot;1550&quot; data-origin-width=&quot;2852&quot; data-origin-height=&quot;1550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;스킨을 적용할 때 에러가 발생하는데 어떤 에러가 발생하는지 알 수 없습니다. 신기하게 에러가 발생해도 스킨은 적용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;1640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bekyWA/dJMcahjGy62/5zntm2ZERihqWnM3ImfJNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bekyWA/dJMcahjGy62/5zntm2ZERihqWnM3ImfJNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bekyWA/dJMcahjGy62/5zntm2ZERihqWnM3ImfJNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbekyWA%2FdJMcahjGy62%2F5zntm2ZERihqWnM3ImfJNk%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;1978&quot; height=&quot;1640&quot; data-origin-width=&quot;1978&quot; data-origin-height=&quot;1640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>기타영역 공부 기록</category>
      <category>Ai</category>
      <category>Claude</category>
      <category>codex</category>
      <category>티스토리스킨</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/914</guid>
      <comments>https://malwareanalysis.tistory.com/914#entry914comment</comments>
      <pubDate>Mon, 6 Apr 2026 00:03:58 +0900</pubDate>
    </item>
    <item>
      <title>소켓 누수는 어떻게 시스템 전체를 불안정하게 만드는가?</title>
      <link>https://malwareanalysis.tistory.com/913</link>
      <description>&lt;h1&gt;공부목표&lt;/h1&gt;
&lt;p&gt;리눅스에서 소켓 누수 문제에 대해 공부하고 재현합니다.  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;어떻게 소켓 누수 문제를 확인할 수 있는지?  &lt;/li&gt;
&lt;li&gt;소켓 누수가 계속 발생하면 어떤 문제가 발생할 수 있는지?&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;소켓 누수란&lt;/h1&gt;
&lt;p&gt;소켓 누수는 종료되어야 할 소켓이 닫히지 않고 계속 남아있는 현상입니다. 사용한 소켓을 close하지 않으면 발생합니다.&lt;/p&gt;
&lt;h1&gt;소켓과 fd의 관계&lt;/h1&gt;
&lt;p&gt;리눅스는 소켓을 생성할 때 fd(파일 디스크립터)를 할당합니다. 리눅스에서 소켓은 파일로 관리되기 때문입니다.&lt;/p&gt;
&lt;p&gt;/proc/{pid}/fd에서 프로세스가 사용 중인 fd를 확인할 수 있습니다. socket 문자열이 보이는 fd가 소켓입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls -l /proc/{pid}/fd&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/trZUF/dJMcadO1IXh/NKgzsdqvUoBPMI5EbX1Srk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/trZUF/dJMcadO1IXh/NKgzsdqvUoBPMI5EbX1Srk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/trZUF/dJMcadO1IXh/NKgzsdqvUoBPMI5EbX1Srk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtrZUF%2FdJMcadO1IXh%2FNKgzsdqvUoBPMI5EbX1Srk%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;2256&quot; height=&quot;1010&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;fd 고갈과 시스템 장애&lt;/h1&gt;
&lt;p&gt;fd는 무한히 만들 수 없어 소켓 누수가 계속되면 fd 를 고갈시킵니다. 이 영향으로 소켓, 파일 등 입출력 리소스를 생성하지 못하고 &lt;strong&gt;Too many open files 에러가 발생&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3878&quot; data-origin-height=&quot;1796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4diuf/dJMcaf7bTEj/9hBHeKvJftd6HJALTdCZoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4diuf/dJMcaf7bTEj/9hBHeKvJftd6HJALTdCZoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4diuf/dJMcaf7bTEj/9hBHeKvJftd6HJALTdCZoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4diuf%2FdJMcaf7bTEj%2F9hBHeKvJftd6HJALTdCZoK%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;3878&quot; height=&quot;1796&quot; data-origin-width=&quot;3878&quot; data-origin-height=&quot;1796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;소켓 누수를 방치하면 벌어지는 일&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;소켓 누수가 지속되면 fd가 고갈되면서 리눅스에서 실행 중인 모든 프로세스가 영향을 받습니다. 더 나아가서 시스템 전체에 영향을 미쳐 서비스 장애가 발생할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;예를 들어 Java 프로세스에서는 누수된 소켓을 정리하기 위해 GC(가비지 컬렉터)가 빈번하게 실행됩니다. CPU, 메모리등 시스템 리소스 사용률이 함께 올라갑니다.  리소스 사용률이 100%에 육박하면 서버는 불안정상태가 됩니다.&lt;/p&gt;
&lt;p&gt;이 영향으로 서버가 원래 처리해야할 일을 못하고 일들이 남은 서버로 전달되게 됩니다. 남은 서버들이 부하를 견디지 못하면  서비스 장애가 시작됩니다. http프로토콜 관점에서는 남은 서버에서 부하가 발생하면 클라이언트에서 connection timeout이 발생하고 재시도를 실행하는 retry storm 현상이 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buV4OR/dJMb996Zxj3/nju0VEkK13q6n4LOUSzDjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buV4OR/dJMb996Zxj3/nju0VEkK13q6n4LOUSzDjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buV4OR/dJMb996Zxj3/nju0VEkK13q6n4LOUSzDjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuV4OR%2FdJMb996Zxj3%2Fnju0VEkK13q6n4LOUSzDjk%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;2816&quot; height=&quot;1536&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;테스트&lt;/h2&gt;
&lt;p&gt;mariadb-java-client 2.7.2에서 SocketException 발생 시 소켓을 close하지 않는 버그를 이용해 소켓 누수를 재현했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실습자료: &lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/computer_science/leak_linux_socket/docs/hands-on.md&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/computer_science/leak_linux_socket/docs/hands-on.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;실습 환경&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MariaDB 10.11 컨테이너&lt;/li&gt;
&lt;li&gt;Spring Boot 2.7.18 앱 컨테이너 (커넥션 풀 없이 Raw JDBC 직접 사용, fd를 64로 제한)&lt;/li&gt;
&lt;li&gt;mariadb-java-client 2.7.2 (예외 시 소켓을 닫지 않는 드라이버)&lt;/li&gt;
&lt;li&gt;socketTimeout=3000 (3초 제한)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;결과&lt;/h2&gt;
&lt;p&gt;k6로 부하를 발생시키고 Prometheus, Grafana로 모니터링했습니다. 부하가 발생하자 fd 사용률이 거의 100%에 도달했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1868&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXoO4y/dJMcah419k4/4WrmzYpkkcgGJ023Ws2Y5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXoO4y/dJMcah419k4/4WrmzYpkkcgGJ023Ws2Y5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXoO4y/dJMcah419k4/4WrmzYpkkcgGJ023Ws2Y5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXoO4y%2FdJMcah419k4%2F4WrmzYpkkcgGJ023Ws2Y5k%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;1868&quot; height=&quot;724&quot; data-origin-width=&quot;1868&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Spring Boot 컨테이너를 확인하면 fd가 대량으로 쌓여 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2640&quot; data-origin-height=&quot;2584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blAJbD/dJMcaiW7tIG/G9mSzn0gBxUuiME6Wv5uxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blAJbD/dJMcaiW7tIG/G9mSzn0gBxUuiME6Wv5uxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blAJbD/dJMcaiW7tIG/G9mSzn0gBxUuiME6Wv5uxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblAJbD%2FdJMcaiW7tIG%2FG9mSzn0gBxUuiME6Wv5uxK%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;2640&quot; height=&quot;2584&quot; data-origin-width=&quot;2640&quot; data-origin-height=&quot;2584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;소켓 상태가 전부 ESTABLISHED였습니다. 닫혀야 할 소켓이 닫히지 않고 남아있는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4844&quot; data-origin-height=&quot;1846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVy2Xf/dJMcahqqFNL/6uvkOKnYYza5jQbGsfVHhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVy2Xf/dJMcahqqFNL/6uvkOKnYYza5jQbGsfVHhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVy2Xf/dJMcahqqFNL/6uvkOKnYYza5jQbGsfVHhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVy2Xf%2FdJMcahqqFNL%2F6uvkOKnYYza5jQbGsfVHhK%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;4844&quot; height=&quot;1846&quot; data-origin-width=&quot;4844&quot; data-origin-height=&quot;1846&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;fd가 고갈되면서 Too many open files 에러가 발생했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3878&quot; data-origin-height=&quot;1796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhpXQ9/dJMcahjGysr/Lviku6a08Rmv4msVQlU2zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhpXQ9/dJMcahjGysr/Lviku6a08Rmv4msVQlU2zK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhpXQ9/dJMcahjGysr/Lviku6a08Rmv4msVQlU2zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhpXQ9%2FdJMcahjGysr%2FLviku6a08Rmv4msVQlU2zK%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;3878&quot; height=&quot;1796&quot; data-origin-width=&quot;3878&quot; data-origin-height=&quot;1796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Too many open files 에러는 시간이 갈수록 증가했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mDTwU/dJMcacJq6aL/xjXLKtBj6tyPVCexNkthhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mDTwU/dJMcacJq6aL/xjXLKtBj6tyPVCexNkthhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mDTwU/dJMcacJq6aL/xjXLKtBj6tyPVCexNkthhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmDTwU%2FdJMcacJq6aL%2FxjXLKtBj6tyPVCexNkthhk%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;4116&quot; height=&quot;942&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;JVM에서는 GC 발생 빈도가 증가했습니다. 제가 JVM GC 동작을 잘 모르는데, 누수된 소켓을 정리하기 위해 GC가 계속 실행되는 것 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4082&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coPCI0/dJMcahjGys2/OCGcjpFO9iCAJEXiSX30lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coPCI0/dJMcahjGys2/OCGcjpFO9iCAJEXiSX30lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coPCI0/dJMcahjGys2/OCGcjpFO9iCAJEXiSX30lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoPCI0%2FdJMcahjGys2%2FOCGcjpFO9iCAJEXiSX30lk%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;4082&quot; height=&quot;912&quot; data-origin-width=&quot;4082&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>cs</category>
      <category>Linux</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/913</guid>
      <comments>https://malwareanalysis.tistory.com/913#entry913comment</comments>
      <pubDate>Sun, 5 Apr 2026 23:24:47 +0900</pubDate>
    </item>
    <item>
      <title>Access Key 없이 IAM user가 AWS CLI 쓰는 방법(aws login)</title>
      <link>https://malwareanalysis.tistory.com/912</link>
      <description>&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-2565470332202567&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h1 id=&quot;해결하려는-문제&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;해결하려는 문제&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AWS는 임시 자격증명(temporary credentials)을 사용하라고 권장합니다. 하지만 로컬에서 개발할 때는 assume 등을 위해 Access Key가 필요했습니다. 그래서 울며 겨자 먹기로 Access Key를 생성하고 주기적으로 철회하는 로직을 관리자는 구현해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 이 관리로직을 설정안해도 됩니다! &lt;b&gt;2025년 11월 AWS에서 IAM user도 임시 자격증명을 발급받는&lt;span&gt;&amp;nbsp;&lt;/span&gt;aws login&lt;span&gt;&amp;nbsp;&lt;/span&gt;기능을 출시&lt;/b&gt;했습니다. OAuth 기능을 사용하여 임시 자격증명을 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;준비&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;준비&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AWS IAM user는&lt;span&gt;&amp;nbsp;&lt;/span&gt;SignInLocalDevelopmentAccess&lt;span&gt;&amp;nbsp;&lt;/span&gt;IAM policy를 사용할 수 있어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1868&quot; data-origin-height=&quot;1446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y0Q6F/dJMcahDP5aT/qm11f7C64lPxlBvoWg9h81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y0Q6F/dJMcahDP5aT/qm11f7C64lPxlBvoWg9h81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y0Q6F/dJMcahDP5aT/qm11f7C64lPxlBvoWg9h81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy0Q6F%2FdJMcahDP5aT%2Fqm11f7C64lPxlBvoWg9h81%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;1868&quot; height=&quot;1446&quot; data-origin-width=&quot;1868&quot; data-origin-height=&quot;1446&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;h1 id=&quot;사용-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;사용 방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AWS CLI v2.32.0 이상이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;로그인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;로그인&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;aws login&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 사용하면 자동으로 웹 브라우저가 열리고, 임시 자격증명을 받기 위한 OAuth flow가 진행됩니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;aws login&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3398&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bodE4K/dJMcaakq7Xw/ck9xE8mDPAlKK3dgmk57A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bodE4K/dJMcaakq7Xw/ck9xE8mDPAlKK3dgmk57A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bodE4K/dJMcaakq7Xw/ck9xE8mDPAlKK3dgmk57A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbodE4K%2FdJMcaakq7Xw%2Fck9xE8mDPAlKK3dgmk57A1%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;3398&quot; height=&quot;430&quot; data-origin-width=&quot;3398&quot; data-origin-height=&quot;430&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AWS Console에 이미 로그인되었다면 로그인된 세션 목록이 보이고, 로그인이 안 되어 있다면 AWS 로그인 과정이 뜹니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD4odL/dJMcaakq7XP/vSwZIsKRZFPcsSKdTeIc7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD4odL/dJMcaakq7XP/vSwZIsKRZFPcsSKdTeIc7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD4odL/dJMcaakq7XP/vSwZIsKRZFPcsSKdTeIc7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD4odL%2FdJMcaakq7XP%2FvSwZIsKRZFPcsSKdTeIc7K%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;1858&quot; height=&quot;968&quot; data-origin-width=&quot;1858&quot; data-origin-height=&quot;968&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세션을 선택하면 액세스 토큰 발급이 끝납니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi5dE4/dJMcadOV6Yg/vBuDSZG9umtVimM038Kcj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi5dE4/dJMcadOV6Yg/vBuDSZG9umtVimM038Kcj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi5dE4/dJMcadOV6Yg/vBuDSZG9umtVimM038Kcj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi5dE4%2FdJMcadOV6Yg%2FvBuDSZG9umtVimM038Kcj1%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;1718&quot; height=&quot;504&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;504&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;액세스 토큰은&lt;span&gt;&amp;nbsp;&lt;/span&gt;~/.aws/login/cache에 저장됩니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;ls ~/.aws/login/cache&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnh3yd/dJMcadOV6Yt/VSbQ0jFqoBaC7KwsV6cxUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnh3yd/dJMcadOV6Yt/VSbQ0jFqoBaC7KwsV6cxUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnh3yd/dJMcadOV6Yt/VSbQ0jFqoBaC7KwsV6cxUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnh3yd%2FdJMcadOV6Yt%2FVSbQ0jFqoBaC7KwsV6cxUk%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;1882&quot; height=&quot;152&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2572&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9nZiI/dJMcahcMJZD/aLEdMNjOmMGT3KayUHoEK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9nZiI/dJMcahcMJZD/aLEdMNjOmMGT3KayUHoEK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9nZiI/dJMcahcMJZD/aLEdMNjOmMGT3KayUHoEK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9nZiI%2FdJMcahcMJZD%2FaLEdMNjOmMGT3KayUHoEK1%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;2572&quot; height=&quot;1186&quot; data-origin-width=&quot;2572&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;멀티-계정-프로필&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;멀티 계정 프로필&lt;/h2&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;aws login --profile prod-admin
aws login --profile staging-dev
aws s3 ls --profile staging-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;로그아웃&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;로그아웃&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;aws logout은 발급받은 액세스 토큰을 15분 안에 만료시킵니다.&lt;/p&gt;
&lt;div id=&quot;cb4&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;aws logout           # default 프로필
aws logout --all     # 전체 프로필&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3428&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cylRzG/dJMcahqk0P7/mKVAwvibtsV1kuAESNLSA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cylRzG/dJMcahqk0P7/mKVAwvibtsV1kuAESNLSA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cylRzG/dJMcahqk0P7/mKVAwvibtsV1kuAESNLSA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcylRzG%2FdJMcahqk0P7%2FmKVAwvibtsV1kuAESNLSA0%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;3428&quot; height=&quot;184&quot; data-origin-width=&quot;3428&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;로그아웃&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;웹브라우저를 못쓰는 곳이라면?&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹브라우저를 못쓰는 곳이어도 aws login을 쓸 수 있습니다. remote파라미터를 사용하면 웹브라우저를 쓸 수 있는곳에서 접속할 수 있는 URL이 나옵니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1774785311462&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws login --remote&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2994&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/butil0/dJMcabp8pvT/A0FrWbETBwyWiLxKvwBzB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/butil0/dJMcabp8pvT/A0FrWbETBwyWiLxKvwBzB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/butil0/dJMcabp8pvT/A0FrWbETBwyWiLxKvwBzB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbutil0%2FdJMcabp8pvT%2FA0FrWbETBwyWiLxKvwBzB0%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;2994&quot; height=&quot;566&quot; data-origin-width=&quot;2994&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crg6IE/dJMcagyaeY3/n75rr1K0wluNZPw9sGxKmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crg6IE/dJMcagyaeY3/n75rr1K0wluNZPw9sGxKmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crg6IE/dJMcagyaeY3/n75rr1K0wluNZPw9sGxKmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrg6IE%2FdJMcagyaeY3%2Fn75rr1K0wluNZPw9sGxKmk%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;1486&quot; height=&quot;940&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;940&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;h1 id=&quot;기대효과&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;기대효과&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;aws login을 사용하면 관리자는 Access Key 로테이션 등 관리를 안 해도 됩니다. 사용자는&lt;span&gt;&amp;nbsp;&lt;/span&gt;$HOME/.aws/credentials&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일을 더 이상 사용하지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;$HOME/.aws/config&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일만 사용하면 됩니다.&lt;/p&gt;
&lt;div id=&quot;cb5&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[default]
region = ap-northeast-2
output = json

[profile prod-admin]
region = ap-northeast-2
output = json&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;aws-login-원리&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;aws login 원리&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로 OAuth 2.0 + PKCE(Proof Key for Code Exchange) 흐름을 사용합니다. OAuth의 동작은 제가 이전에 작성한 블로그를 참고하시면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth 원리:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://malwareanalysis.tistory.com/889&quot;&gt;https://malwareanalysis.tistory.com/889&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcEdBQ/dJMcad2tDhx/KA8oRCrp8X9y7EGUWvfaik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcEdBQ/dJMcad2tDhx/KA8oRCrp8X9y7EGUWvfaik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcEdBQ/dJMcad2tDhx/KA8oRCrp8X9y7EGUWvfaik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcEdBQ%2FdJMcad2tDhx%2FKA8oRCrp8X9y7EGUWvfaik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1636&quot; height=&quot;1178&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;참고자료&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;참고자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;aws login 출시:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://aws.amazon.com/ko/about-aws/whats-new/2025/11/console-credentials-aws-cli-sdk-authentication/&quot;&gt;https://aws.amazon.com/ko/about-aws/whats-new/2025/11/console-credentials-aws-cli-sdk-authentication/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;aws login 사용방법:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/security/simplified-developer-access-to-aws-with-aws-login/&quot;&gt;https://aws.amazon.com/ko/blogs/security/simplified-developer-access-to-aws-with-aws-login/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>aws</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/912</guid>
      <comments>https://malwareanalysis.tistory.com/912#entry912comment</comments>
      <pubDate>Sun, 29 Mar 2026 20:38:14 +0900</pubDate>
    </item>
    <item>
      <title>custom EKS AMI 만들기 - Managed Node Group과 Karpenter 설정 차이</title>
      <link>https://malwareanalysis.tistory.com/911</link>
      <description>&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;a id=&quot;toc-해결하려는-문제&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#해결하려는-문제&quot;&gt;해결하려는 문제&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-ami를-생성하는-방법&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#ami를-생성하는-방법&quot;&gt;AMI를 생성하는 방법&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-custom-ami로-만든-node가-클러스터에-조인할-수-있을까&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#custom-ami로-만든-node가-클러스터에-조인할-수-있을까&quot;&gt;custom AMI로 만든 node가 클러스터에 조인할 수 있을까?&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-managed-node-group에서-custom-ami-사용&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#managed-node-group에서-custom-ami-사용&quot;&gt;Managed Node Group에서 custom AMI 사용&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-karpenter에서-custom-ami-사용&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#karpenter에서-custom-ami-사용&quot;&gt;Karpenter에서 custom AMI 사용&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-custom-ami를-만들때-주의-사항&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#custom-ami를-만들때-주의-사항&quot;&gt;custom AMI를 만들때 주의 사항&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;해결하려는-문제&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;해결하려는 문제&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;EKS node에 모니터링 에이전트, 보안 패키지 등 추가 소프트웨어를 설치해야 하는 경우가 있습니다. 간단한 작업이면 user data로 충분합니다. 하지만 설치할 내용이 많아지면 두 가지 문제가 생깁니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;user data가 비대해져서 관리가 어려워집니다&lt;/li&gt;
&lt;li&gt;node가 부팅될 때마다 패키지를 설치하므로 node 생성 속도가 느려집니다&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하는 방법이 custom EKS AMI입니다. 필요한 패키지를 미리 설치한 AMI를 만들어 두면, node 생성 시 추가 설치 없이 바로 사용할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ami를-생성하는-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;EKS custom AMI설정 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Managed Node Group은 custom AMI 사용 시 user data에 NodeConfig를 직접 설정해야 합니다&lt;/li&gt;
&lt;li&gt;Karpenter는 NodeConfig를 자동 생성하므로 별도 설정 없이 AMI ID만 지정하면 됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;ami를-생성하는-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;AMI를 생성하는 방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AMI를 생성하는 방법은  본인의 환경마다 다릅니다. 보통 Packer만 사용하거나 Packer + Ansible 조합을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;base AMI는 EKS optimized AMI를 사용하는 편이 작업하기 편합니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;EKS optimized AMI에는 nodeadm, kubelet, containerd 등 node가 클러스터에 조인하는 데 필요한 패키지가 설치되어 있습니다. nodeadm은 AL2023 EKS AMI에서 node를 클러스터에 조인시키는 도구입니다. 임의의 AMI를 base로 사용하면 클러스터 조인이 실패합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;EKS optimized AMI는 GitHub release에서 찾는 것이 쉽습니다.&lt;br /&gt;- EKS optimized AMI release:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/awslabs/amazon-eks-ami/releases&quot;&gt;https://github.com/awslabs/amazon-eks-ami/releases&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글의 예제에서는 간단히 Packer와 shell script를 사용했습니다.&lt;br /&gt;- Packer 예제:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/portfolio/blob/master/aws/eks-custom-ami/packer/eks-custom-ami.pkr.hcl&quot;&gt;https://github.com/choisungwook/portfolio/blob/master/aws/eks-custom-ami/packer/eks-custom-ami.pkr.hcl&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;&lt;code&gt;build {
  name    = &quot;eks-custom-ami&quot;
  sources = [&quot;source.amazon-ebs.eks&quot;]

  provisioner &quot;shell&quot; {
    script = &quot;${path.root}/scripts/setup.sh&quot;
  }
}

... 이하생 략&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;custom-ami로-만든-node가-클러스터에-조인할-수-있을까&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;custom AMI로 만든 node가 클러스터에 조인할 수 있을까?&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;custom EKS AMI를 만들 때 가장 먼저 드는 고민은 &amp;ldquo;이 AMI로 만든 node가 EKS 클러스터에 조인할 수 있나?&amp;rdquo;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결과만 먼저 말씀드리면 node를 어떻게 생성하느냐에 따라 설정 방법이 다릅니다. EKS node는 Managed Node Group으로 관리되는 EC2와 Karpenter로 생성되는 EC2가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;managed-node-group에서-custom-ami-사용&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;Managed Node Group에서 custom AMI 사용&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Managed Node Group로 EKS custom AMI를 사용하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사용자가 직접 user data에 NodeConfig를 설정해야 해야합니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;NodeConfig는 nodeadm이 EKS에 조인할 때 사용하는 설정 파일입니다. MIME 타입으로 설정해야 하며, 필수 파라미터가 필요합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;name&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; EKS 클러스터 이름&lt;/li&gt;
&lt;li&gt;apiServerEndpoint&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; EKS 클러스터 API endpoint&lt;/li&gt;
&lt;li&gt;certificateAuthority&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 클러스터 CA 인증서 (base64)&lt;/li&gt;
&lt;li&gt;cidr&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 서비스 CIDR&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;http&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;&lt;code&gt;MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=&quot;BOUNDARY&quot;

--BOUNDARY
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: &amp;lt;클러스터 이름&amp;gt;
    apiServerEndpoint: &amp;lt;API endpoint&amp;gt;
    certificateAuthority: &amp;lt;CA 인증서&amp;gt;
    cidr: &amp;lt;서비스 CIDR&amp;gt;

--BOUNDARY--&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Terraform을 사용한다면 아래 예제처럼 Terraform 리소스를 참조하여 자동화할 수 있습니다.&lt;br /&gt;- Terraform 예제:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/choisungwook/terraform_practice/pull/117/changes&quot;&gt;https://github.com/choisungwook/terraform_practice/pull/117/changes&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;karpenter에서-custom-ami-사용&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;Karpenter에서 custom AMI 사용&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Karpenter에서 custom AMI를 사용하는 방법은 간단합니다. EC2NodeClass에서 AMI ID를 지정&lt;/b&gt;하면 됩니다. Managed Node Group과 달리, Karpenter는 NodeConfig를 자동으로 생성합니다. 사용자가 직접 user data에 NodeConfig를 작성할 필요가 없습니다.&lt;/p&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: custom-ami
spec:
  amiFamily: AL2023
  amiSelectorTerms:
    - id: &quot;${CUSTOM_AMI_ID}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;custom-ami를-만들때-주의-사항&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;custom AMI를 만들때 주의 사항&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;custom AMI를 만들 때 추가 패키지만 설치하고, EKS 핵심 컴포넌트는 건드리지 않아야 합니다. 이 컴포넌트들은 EKS 버전에 맞게 사전 구성되어 있습니다. 변경하면 node 조인이 실패할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nodeadm을 삭제하거나 업데이트하지 않습니다&lt;/li&gt;
&lt;li&gt;kubelet, containerd 버전을 변경하지 않습니다&lt;/li&gt;
&lt;li&gt;/etc/eks/&lt;span&gt;&amp;nbsp;&lt;/span&gt;하위 기존 설정 파일을 수정하지 않습니다&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>전공영역 공부 기록</category>
      <category>EKS</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/911</guid>
      <comments>https://malwareanalysis.tistory.com/911#entry911comment</comments>
      <pubDate>Sun, 29 Mar 2026 18:46:46 +0900</pubDate>
    </item>
    <item>
      <title>cafe24 wordpress를 cloudflare연동할 때 주의할점, ssl 인증서 갱신 오류</title>
      <link>https://malwareanalysis.tistory.com/910</link>
      <description>&lt;div&gt;
&lt;div&gt;&lt;b&gt;Cafe24에서 구매한 도메인의 NS 레코드를 Cloudflare에 위임하면 Cafe24 SSL 인증서 자동 갱신이 중단&lt;/b&gt;됩니다. Cloudflare SSL 모드를 Flexible로 변경하고 WordPress 설정을 수정하여 해결하는 방법을 정리합니다.&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-만난-오류&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#만난-오류&quot;&gt;만난 오류&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-오류-원인&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#오류-원인&quot;&gt;오류 원인&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-오류-해결-방법&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#오류-해결-방법&quot;&gt;오류 해결 방법&lt;/a&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;a id=&quot;toc-단계.-cloudflare에서-wordpress-호출을-http로-변경&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#단계.-cloudflare에서-wordpress-호출을-http로-변경&quot;&gt;1단계. Cloudflare에서 WordPress 호출을 HTTP로 변경&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-단계.-wordpress의-wp-config.php-설정&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#단계.-wordpress의-wp-config.php-설정&quot;&gt;2단계. WordPress의 wp-config.php 설정&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a id=&quot;toc-단계.-ip-접근-제한&quot; style=&quot;color: #1a1a1a;&quot; href=&quot;#단계.-ip-접근-제한&quot;&gt;3단계. IP 접근 제한&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;만난-오류&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;만난 오류&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cafe24에서 구매한 도메인을 Cloudflare NS에 등록해서 WordPress 블로그를 운영하고 있었습니다. 잘 되다가 어느 시점 이후부터 Cafe24 SSL 인증서 갱신이 실패하면서 Cloudflare에서 Origin SSL 에러가 발생했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2442&quot; data-origin-height=&quot;1744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvGLxb/dJMcajhkeAu/gOUgyh93OFl5ohP2z579H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvGLxb/dJMcajhkeAu/gOUgyh93OFl5ohP2z579H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvGLxb/dJMcajhkeAu/gOUgyh93OFl5ohP2z579H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvGLxb%2FdJMcajhkeAu%2FgOUgyh93OFl5ohP2z579H1%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;2442&quot; height=&quot;1744&quot; data-origin-width=&quot;2442&quot; data-origin-height=&quot;1744&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;h1 id=&quot;오류-원인&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;오류 원인&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cloudflare에 Cafe24 도메인의 NS 레코드 관리를 위임한 것이 원인이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cafe24는 자사에서 구매한 도메인을 Cafe24가 직접 관리해야만 SSL 인증서를 자동 갱신&lt;/b&gt;합니다. NS 레코드 관리를 Cloudflare에 위임하면 Cafe24 입장에서는 관리 대상 도메인으로 취급하지 않아 SSL 인증서 갱신을 하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 인증서 갱신일이 되기 전까지는 WordPress가 정상 접속되었지만, 인증서가 만료되면서 에러가 발생한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;오류-해결-방법&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;오류 해결 방법&lt;/h1&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cloudflare에서 Cafe24 WordPress를 호출할 때 HTTP로 변경&lt;/b&gt;해야 합니다. Cafe24에서 인증서 갱신을 못하니 어쩔 수 없이 HTTP 통신으로 변경합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;WordPress 최초 진입 구간은 Cloudflare이고, Cloudflare가 HTTPS 통신을 하기 때문에 WordPress를 HTTP로 변경해도 보안에 문제될 건 없어 보였습니다. 다만, WordPress는 Cafe24만 접속할 수 있도록 IP 접근 제한을 걸어야 합니다. WordPress 주소에 HTTP로 누구나 접근할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;WordPress를 HTTP로 호출하려면 3단계를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;단계.-cloudflare에서-wordpress-호출을-http로-변경&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1단계. Cloudflare에서 WordPress 호출을 HTTP로 변경&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cloudflare 메뉴에서 SSL/TLS -&amp;gt; Overview -&amp;gt; Flexible로 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3106&quot; data-origin-height=&quot;1982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ldlvb/dJMcabQ948T/nmOkdbd4TrAkzneikxfoo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ldlvb/dJMcabQ948T/nmOkdbd4TrAkzneikxfoo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ldlvb/dJMcabQ948T/nmOkdbd4TrAkzneikxfoo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fldlvb%2FdJMcabQ948T%2FnmOkdbd4TrAkzneikxfoo1%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;3106&quot; height=&quot;1982&quot; data-origin-width=&quot;3106&quot; data-origin-height=&quot;1982&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Flexible은 Cloudflare가 원본 서버(WordPress)를 호출할 때 HTTP로 호출하는 모드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPvQyc/dJMcajn3Sjw/BnFCLuto8cfMkiHtLPulzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPvQyc/dJMcajn3Sjw/BnFCLuto8cfMkiHtLPulzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPvQyc/dJMcajn3Sjw/BnFCLuto8cfMkiHtLPulzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPvQyc%2FdJMcajn3Sjw%2FBnFCLuto8cfMkiHtLPulzK%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;1188&quot; height=&quot;370&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;단계.-wordpress의-wp-config.php-설정&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2단계. WordPress의 wp-config.php 설정&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;WordPress를 HTTP로 설정하면 이미지 등 에셋이 보이지 않게 됩니다. 에셋 URL이 HTTPS 기준으로 되어 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하려면 C&lt;b&gt;loudflare가 HTTP로 호출하더라도 WordPress가 HTTPS로 요청받은 것처럼 동작하게 설정&lt;/b&gt;해야 합니다. 그래야 WordPress가 반환하는 에셋 URL이 HTTPS로 응답됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lXByX/dJMcabKrkKK/eY3dhhXh10qWbuECH0KN40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lXByX/dJMcabKrkKK/eY3dhhXh10qWbuECH0KN40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lXByX/dJMcabKrkKK/eY3dhhXh10qWbuECH0KN40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlXByX%2FdJMcabKrkKK%2FeY3dhhXh10qWbuECH0KN40%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;1984&quot; height=&quot;452&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;452&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 style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;wp-config.php에서 아래 코드를 추가합니다.&lt;/p&gt;
&lt;div id=&quot;cb1&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;if (isset($_SERVER['HTTP_CF_VISITOR'])) {
  $visitor = json_decode($_SERVER['HTTP_CF_VISITOR']);
  if ($visitor &amp;amp;&amp;amp; $visitor-&amp;gt;scheme === 'https') {
    $_SERVER['HTTPS'] = 'on';
  }
}

define('WP_HOME', 'https://{yourdomain}');
define('WP_SITEURL', 'https://{yourdomain}');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 이미지 등 에셋이 안 보여도 괜찮다면 아래처럼 설정하면 됩니다.&lt;/p&gt;
&lt;div id=&quot;cb2&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;define('WP_HOME', 'http://{your-domain}');
define('WP_SITEURL', 'http://{your-domain}');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;단계.-ip-접근-제한&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3단계. IP 접근 제한&lt;/h2&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;WordPress는 Cloudflare만 접근하도록 설정합니다. Cafe24 WordPress는 Apache로 호스팅하므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;.htaccess&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일에 Cloudflare IP를 추가하면 됩니다.&lt;/p&gt;
&lt;div id=&quot;cb3&quot; style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;IfModule mod_authz_core.c&amp;gt;
      Require ip 173.245.48.0/20
      Require ip 103.21.244.0/22
      Require ip 103.22.200.0/22
      Require ip 103.31.4.0/22
      Require ip 141.101.64.0/18
      Require ip 108.162.192.0/18
      Require ip 190.93.240.0/20
      Require ip 188.114.96.0/20
      Require ip 197.234.240.0/22
      Require ip 198.41.128.0/17
      Require ip 162.158.0.0/15
      Require ip 104.16.0.0/13
      Require ip 104.24.0.0/14
      Require ip 172.64.0.0/13
      Require ip 131.0.72.0/22
&amp;lt;/IfModule&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cloudflare IP는 아래 페이지에서 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cloudflare.com/ips/&quot;&gt;https://www.cloudflare.com/ips/&lt;/a&gt;&lt;br /&gt;&lt;br /&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;2772&quot; data-origin-height=&quot;1460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y67T7/dJMcagZeyYL/2UxfROq6TaVunBOxolq3uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y67T7/dJMcagZeyYL/2UxfROq6TaVunBOxolq3uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y67T7/dJMcagZeyYL/2UxfROq6TaVunBOxolq3uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY67T7%2FdJMcagZeyYL%2F2UxfROq6TaVunBOxolq3uK%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;2772&quot; height=&quot;1460&quot; data-origin-width=&quot;2772&quot; data-origin-height=&quot;1460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdfdfd; color: #1a1a1a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cloudflare IP 주소는 수시로 변경될 수 있으니 update history를 정기적으로 확인&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;3160&quot; data-origin-height=&quot;1444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b56wXv/dJMcaf65XXX/PcvY9bU7x0VdUn4BKgVSdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b56wXv/dJMcaf65XXX/PcvY9bU7x0VdUn4BKgVSdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b56wXv/dJMcaf65XXX/PcvY9bU7x0VdUn4BKgVSdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb56wXv%2FdJMcaf65XXX%2FPcvY9bU7x0VdUn4BKgVSdk%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;3160&quot; height=&quot;1444&quot; data-origin-width=&quot;3160&quot; data-origin-height=&quot;1444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>기타영역 공부 기록</category>
      <category>CloudFlare</category>
      <category>Wordpress</category>
      <author>악분</author>
      <guid isPermaLink="true">https://malwareanalysis.tistory.com/910</guid>
      <comments>https://malwareanalysis.tistory.com/910#entry910comment</comments>
      <pubDate>Sat, 28 Mar 2026 22:30:33 +0900</pubDate>
    </item>
  </channel>
</rss>