<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>sojsoj 님의 블로그</title>
    <link>https://sojsoj.tistory.com/</link>
    <description>sojsoj 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 8 Aug 2026 11:16:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>sojsoj</managingEditor>
    <item>
      <title>16일차_Hugging Face</title>
      <link>https://sojsoj.tistory.com/15</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Hugging Face란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/&quot;&gt;&lt;span&gt;&lt;span&gt;Hugging Face&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;는 AI 모델을 검색하고, 시험하고, 내려받고, 공유하는 플랫폼입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 &amp;ldquo;AI 분야의 GitHub&amp;rdquo;라고 부릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Models&lt;/b&gt;: 텍스트&amp;middot;이미지&amp;middot;음성&amp;middot;영상 AI 모델&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Datasets&lt;/b&gt;: AI 학습용 데이터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spaces&lt;/b&gt;: 브라우저에서 실행해 볼 수 있는 AI 데모&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Papers&lt;/b&gt;: 최신 AI 논문&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Collections&lt;/b&gt;: 목적별 모델 모음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face의 &lt;b&gt;Inference Providers&lt;/b&gt;는 무료 계정에 매달 소액의 체험 크레딧을 제공합니다. 현재 공식 문서에는 무료 사용자가 받는 크레딧이 &lt;b&gt;월 $0.10이며 변경될 수 있다&lt;/b&gt;고 명시돼 있습니다. 크레딧을 소진하면 추가 크레딧 또는 별도 공급자 결제가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/docs/inference-providers/en/pricing&quot;&gt;&lt;span&gt;&lt;span&gt;공식 요금 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;2. 목적별 무료 다운로드 가능 모델&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 목록은 대표적인 공개&amp;middot;오픈 가중치 모델입니다. API 무료 제공을 보장하는 목록은 아니며, PC 사양과 라이선스를 반드시 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;텍스트 생성&amp;middot;챗봇&amp;middot;코딩&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/Qwen/Qwen3-4B&quot;&gt;&lt;span&gt;&lt;span&gt;Qwen/Qwen3-4B&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;다국어&amp;middot;한국어&amp;middot;코딩&amp;middot;추론, 비교적 작은 4B 모델&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/Qwen/Qwen3-0.6B&quot;&gt;&lt;span&gt;&lt;span&gt;Qwen/Qwen3-0.6B&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;저사양 PC 실습용&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/google/gemma-3-4b-it&quot;&gt;&lt;span&gt;&lt;span&gt;google/gemma-3-4b-it&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;이미지와 텍스트 이해가 가능한 소형 멀티모달 모델&lt;/td&gt;
&lt;td&gt;Google 이용약관 동의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/microsoft/Phi-4-mini-instruct&quot;&gt;&lt;span&gt;&lt;span&gt;microsoft/Phi-4-mini-instruct&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;작은 크기, 지시 수행과 코딩&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B&quot;&gt;&lt;span&gt;&lt;span&gt;deepseek-ai/DeepSeek-R1-Distill-Qwen-7B&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;수학&amp;middot;논리 추론 특화&lt;/td&gt;
&lt;td&gt;비교적 높은 메모리 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;초보자에게는 Qwen3-0.6B 또는 Qwen3-4B가 편합니다. Qwen3-4B는 100개 이상의 언어를 지원하며 Apache-2.0 라이선스로 공개돼 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://huggingface.co/Qwen/Qwen3-4B&quot;&gt;&lt;span&gt;&lt;span&gt;모델 설명&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;텍스트 &amp;rarr; 이미지&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/black-forest-labs/FLUX.1-schnell&quot;&gt;&lt;span&gt;&lt;span&gt;black-forest-labs/FLUX.1-schnell&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;빠르고 품질이 좋은 이미지 생성&lt;/td&gt;
&lt;td&gt;12B 모델로 GPU 메모리 요구량이 큼, 접근 조건 동의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0&quot;&gt;&lt;span&gt;&lt;span&gt;stabilityai/stable-diffusion-xl-base-1.0&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;자료와 예제가 풍부한 SDXL&lt;/td&gt;
&lt;td&gt;라이선스 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/stabilityai/sd-turbo&quot;&gt;&lt;span&gt;&lt;span&gt;stabilityai/sd-turbo&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;적은 단계로 빠르게 생성&lt;/td&gt;
&lt;td&gt;세밀한 품질은 SDXL과 차이 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;FLUX.1-schnell은 Apache-2.0 모델이지만 파일 접근 전 조건 동의가 필요할 수 있습니다. &lt;a href=&quot;https://huggingface.co/black-forest-labs/FLUX.1-schnell&quot;&gt;&lt;span&gt;&lt;span&gt;모델 페이지&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이미지 &amp;rarr; 이미지&amp;middot;사진 편집&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/Qwen/Qwen-Image-Edit&quot;&gt;&lt;span&gt;&lt;span&gt;Qwen/Qwen-Image-Edit&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;자연어로 사진 수정, 물체&amp;middot;스타일 변경&lt;/td&gt;
&lt;td&gt;고성능 GPU 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/timbrooks/instruct-pix2pix&quot;&gt;&lt;span&gt;&lt;span&gt;timbrooks/instruct-pix2pix&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&amp;ldquo;배경을 밤으로 바꿔줘&amp;rdquo; 같은 지시 기반 편집&lt;/td&gt;
&lt;td&gt;비교적 오래됐지만 예제가 많음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/stabilityai/stable-diffusion-2-1-unclip&quot;&gt;&lt;span&gt;&lt;span&gt;stabilityai/stable-diffusion-2-1-unclip&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;이미지를 참고한 변형 생성&lt;/td&gt;
&lt;td&gt;라이선스 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Qwen Image Edit는 Apache-2.0이며 Diffusers에서 이미지와 명령을 함께 입력할 수 있습니다. &lt;a href=&quot;https://huggingface.co/Qwen/Qwen-Image-Edit&quot;&gt;&lt;span&gt;&lt;span&gt;사용 예제&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이미지 &amp;rarr; 텍스트&amp;middot;이미지 이해&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 94px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;a href=&quot;https://huggingface.co/Salesforce/blip-image-captioning-base&quot;&gt;&lt;span&gt;&lt;span&gt;Salesforce/blip-image-captioning-base&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;사진 설명문 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;a href=&quot;https://huggingface.co/Salesforce/blip-vqa-base&quot;&gt;&lt;span&gt;&lt;span&gt;Salesforce/blip-vqa-base&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;사진에 관한 질문에 답변&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;a href=&quot;https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct&quot;&gt;&lt;span&gt;&lt;span&gt;Qwen/Qwen2.5-VL-3B-Instruct&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;이미지&amp;middot;문서&amp;middot;차트 이해&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;a href=&quot;https://huggingface.co/google/gemma-3-4b-it&quot;&gt;&lt;span&gt;&lt;span&gt;google/gemma-3-4b-it&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;이미지와 텍스트를 함께 이해&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;저사양 실습에는 BLIP, 문서&amp;middot;표&amp;middot;복합 이미지 분석에는 Qwen2.5-VL 계열이 알맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;음성 &amp;rarr; 텍스트&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/openai/whisper-small&quot;&gt;&lt;span&gt;&lt;span&gt;openai/whisper-small&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;한국어 포함 99개 언어 음성 인식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/openai/whisper-large-v3-turbo&quot;&gt;&lt;span&gt;&lt;span&gt;openai/whisper-large-v3-turbo&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;정확도와 속도가 좋은 대형 모델&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/facebook/wav2vec2-base-960h&quot;&gt;&lt;span&gt;&lt;span&gt;facebook/wav2vec2-base-960h&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;영어 음성 인식 실습&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Whisper Small은 Apache-2.0이며 automatic-speech-recognition 파이프라인으로 실행할 수 있습니다. &lt;a href=&quot;https://huggingface.co/openai/whisper-small&quot;&gt;&lt;span&gt;&lt;span&gt;모델 사용법&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;텍스트 &amp;rarr; 음성&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/hexgrad/Kokoro-82M&quot;&gt;&lt;span&gt;&lt;span&gt;hexgrad/Kokoro-82M&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;작고 빠른 고품질 음성 합성&lt;/td&gt;
&lt;td&gt;기본적으로 영어 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/suno/bark-small&quot;&gt;&lt;span&gt;&lt;span&gt;suno/bark-small&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;말투&amp;middot;효과음&amp;middot;다국어 음성 생성&lt;/td&gt;
&lt;td&gt;결과가 일정하지 않을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/facebook/mms-tts-kor&quot;&gt;&lt;span&gt;&lt;span&gt;facebook/mms-tts-kor&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;한국어 음성 합성&lt;/td&gt;
&lt;td&gt;자연스러움은 최신 상용 TTS보다 낮을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Kokoro는 82M 크기의 Apache-2.0 오픈 가중치 모델이어서 로컬 실습에 적합합니다. &lt;a href=&quot;https://huggingface.co/hexgrad/Kokoro-82M&quot;&gt;&lt;span&gt;&lt;span&gt;모델 설명&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;영상 생성&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/Wan-AI/Wan2.1-T2V-1.3B&quot;&gt;&lt;span&gt;&lt;span&gt;Wan-AI/Wan2.1-T2V-1.3B&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;텍스트 &amp;rarr; 짧은 영상&lt;/td&gt;
&lt;td&gt;이미지보다 훨씬 많은 GPU 메모리와 시간이 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt&quot;&gt;&lt;span&gt;&lt;span&gt;stabilityai/stable-video-diffusion-img2vid-xt&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;이미지 &amp;rarr; 영상&lt;/td&gt;
&lt;td&gt;라이선스&amp;middot;GPU 요구사항 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/ali-vilab/i2vgen-xl&quot;&gt;&lt;span&gt;&lt;span&gt;ali-vilab/i2vgen-xl&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;이미지 기반 영상 생성&lt;/td&gt;
&lt;td&gt;고성능 GPU 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이미지 분석&amp;middot;분리&amp;middot;깊이 측정&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/facebook/sam2-hiera-small&quot;&gt;&lt;span&gt;&lt;span&gt;facebook/sam2-hiera-small&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;이미지&amp;middot;영상 속 물체 영역 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/depth-anything/Depth-Anything-V2-Small-hf&quot;&gt;&lt;span&gt;&lt;span&gt;depth-anything/Depth-Anything-V2-Small-hf&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;한 장의 사진에서 깊이 추정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/facebook/detr-resnet-50&quot;&gt;&lt;span&gt;&lt;span&gt;facebook/detr-resnet-50&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;사진 속 물체 탐지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;SAM2 Small과 Depth Anything V2 Small은 모두 Apache-2.0으로 공개돼 있습니다. &lt;a href=&quot;https://huggingface.co/facebook/sam2-hiera-small&quot;&gt;&lt;span&gt;&lt;span&gt;SAM2&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;, &lt;a href=&quot;https://huggingface.co/depth-anything/Depth-Anything-V2-Small-hf&quot;&gt;&lt;span&gt;&lt;span&gt;Depth Anything&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;번역&amp;middot;임베딩&amp;middot;검색&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/facebook/nllb-200-distilled-600M&quot;&gt;&lt;span&gt;&lt;span&gt;facebook/nllb-200-distilled-600M&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;다국어 번역&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/intfloat/multilingual-e5-small&quot;&gt;&lt;span&gt;&lt;span&gt;intfloat/multilingual-e5-small&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;한국어 문서 검색&amp;middot;RAG&amp;middot;유사도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2&quot;&gt;&lt;span&gt;&lt;span&gt;sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;다국어 문장 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h1&gt;3. Hugging Face Access Token을 VS Code에 연결하는 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;VS Code 터미널에 Hugging Face 로그인 정보를 저장한 뒤, VS Code에서 실행하는 Python 코드가 해당 Access Token을 자동으로 사용하도록 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Windows와 VS Code 기준입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 venv 부분을 Conda 가상환경 기준으로 바꾸면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: Conda 가상환경 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code에서 File &amp;rarr; Open Folder로 작업 폴더를 엽니다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;C:\AI\huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code에서 Terminal &amp;rarr; New Terminal을 선택한 후 Conda 설치 여부를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;conda --version&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Conda 가상환경 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.11 환경을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;conda create -n huggingface-study python=3.11 -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상환경을 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 활성화되면 터미널 왼쪽에 다음처럼 표시됩니다.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;(huggingface-study)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;conda activate 오류가 발생할 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;conda init powershell&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 VS Code의 터미널을 모두 닫고 새 터미널을 열거나, VS Code를 재시작합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conda 명령 자체가 인식되지 않는다면 VS Code에서 Ctrl+Shift+P를 누르고 다음을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Terminal: Select Default Profile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command Prompt를 선택한 뒤 새 터미널에서 다시 시도할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VS Code Python 환경 선택&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Ctrl+Shift+P를 누릅니다.&lt;/li&gt;
&lt;li&gt;Python: Select Interpreter를 검색합니다.&lt;/li&gt;
&lt;li&gt;다음과 비슷한 환경을 선택합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Python 3.11 ('huggingface-study': conda)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택된 Python 확인:&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;python -c &quot;import sys; print(sys.executable)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 경로에 huggingface-study 또는 Conda 환경 경로가 포함되어야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4단계: 필요한 패키지 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Conda 환경이 활성화됐는지 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study
python --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip를 업데이트합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;python -m pip install --upgrade pip&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face 기본 패키지를 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;python -m pip install --upgrade huggingface_hub transformers accelerate safetensors&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CPU에서 실행하는 경우&lt;/h3&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python -m pip install torch --index-url https://download.pytorch.org/whl/cpu&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 확인:&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;python -c &quot;import torch; print(torch.__version__); print('CUDA:', torch.cuda.is_available())&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 환경이라면 다음처럼 표시되는 것이 정상입니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;CUDA: False&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NVIDIA GPU를 사용하는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU 인식 여부를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;nvidia-smi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU가 표시되면 &lt;a href=&quot;https://pytorch.org/get-started/locally/&quot;&gt;&lt;span&gt;&lt;span&gt;PyTorch 설치 페이지&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 다음 조건을 선택해 설치 명령을 받습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;OS: Windows
Package: Pip
Language: Python
Compute Platform: 자신의 CUDA 환경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;python -c &quot;import torch; print(torch.__version__); print('CUDA:', torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU 사용')&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지 생성&amp;middot;편집 패키지&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;python -m pip install diffusers pillow sentencepiece protobuf&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;음성 처리 패키지&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;python -m pip install librosa soundfile&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 설치 확인&lt;/h3&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;python -c &quot;import torch, transformers, huggingface_hub; print('PyTorch:', torch.__version__); print('Transformers:', transformers.__version__); print('Hub:', huggingface_hub.__version__)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5단계: Access Token으로 로그인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conda 환경을 먼저 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face에 로그인합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;hf auth login&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메뉴에서 다음을 선택합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;Paste an access token&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발급받은 hf_... 토큰을 붙여 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 확인:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;hf auth whoami&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이름이 표시되면 정상적으로 연결된 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그아웃&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;hf auth logout&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다른 토큰으로 다시 로그인&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;hf auth login --force&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6단계: 연결 시험&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conda 환경을 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서 로그인 상태를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;python -c &quot;from huggingface_hub import whoami; print(whoami())&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 설정 파일 다운로드를 시험합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;hf download Qwen/Qwen3-0.6B config.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 내려받아지면 연결과 인증이 모두 정상입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이후 VS Code를 다시 열 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 새 터미널에서 다음 명령만 실행하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate huggingface-study&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 끝났을 때 가상환경 종료:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;conda deactivate&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전체 명령 모음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 환경 기준으로 순서대로 실행할 명령입니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;conda create -n huggingface-study python=3.11 -y
conda activate huggingface-study
python -m pip install --upgrade pip
python -m pip install --upgrade huggingface_hub transformers accelerate safetensors
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
python -m pip install diffusers pillow sentencepiece protobuf
python -m pip install librosa soundfile
hf auth login
hf auth whoami
python -c &quot;from huggingface_hub import whoami; print(whoami())&quot;
hf download Qwen/Qwen3-0.6B config.json&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7단계: 텍스트 AI 실행하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code에서 text_ai.py 파일을 만들고 다음 코드를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from transformers import pipeline

model_id = &quot;Qwen/Qwen3-0.6B&quot;

generator = pipeline(
    task=&quot;text-generation&quot;,
    model=model_id,
    device_map=&quot;auto&quot;,
)

messages = [
    {
        &quot;role&quot;: &quot;user&quot;,
        &quot;content&quot;: &quot;허깅페이스가 무엇인지 초보자에게 한국어로 설명해 줘. /no_think&quot;,
    }
]

result = generator(
    messages,
    max_new_tokens=300,
    do_sample=True,
    temperature=0.7,
)

print(result[0][&quot;generated_text&quot;][-1][&quot;content&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python text_ai.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 실행에는 모델을 내려받기 때문에 시간이 걸립니다. 이후에는 기본적으로 다음 위치의 캐시를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;C:\Users\사용자이름\.cache\huggingface\hub&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8단계: Access Token으로 API 호출하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 PC에서 모델 전체를 실행하지 않고 Hugging Face의 서버에 요청하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api_chat.py 파일을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from huggingface_hub import InferenceClient

# hf auth login으로 저장한 토큰을 자동으로 사용합니다.
client = InferenceClient(provider=&quot;auto&quot;)

response = client.chat.completions.create(
    model=&quot;Qwen/Qwen3-4B&quot;,
    messages=[
        {
            &quot;role&quot;: &quot;user&quot;,
            &quot;content&quot;: &quot;인공지능을 초등학생도 이해할 수 있게 설명해 줘.&quot;,
        }
    ],
    max_tokens=300,
)

print(response.choices[0].message.content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python api_chat.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 현재 Inference Provider에서 제공되지 않으면 다른 API 지원 모델을 선택해야 합니다. API 사용은 무료 크레딧을 소모하며 한도 이후 비용이 발생할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9단계: 환경변수로 토큰 전달하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI에 로그인하지 않고 현재 터미널에서만 토큰을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$env:HF_TOKEN=&quot;hf_발급받은_토큰&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 때 실제 토큰 전체를 출력하지 말고 다음처럼 검사합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;if ($env:HF_TOKEN) { &quot;HF_TOKEN 설정됨&quot; } else { &quot;HF_TOKEN 없음&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Python 프로그램을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python api_chat.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code 터미널을 닫으면 이 환경변수는 사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식적으로 HF_TOKEN 환경변수가 설정돼 있으면 컴퓨터에 저장된 토큰보다 우선합니다. &lt;a href=&quot;https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables&quot;&gt;&lt;span&gt;&lt;span&gt;환경변수 설명&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;피해야 하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 토큰을 Python 코드에 직접 쓰지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# 사용 금지
token = &quot;hf_실제토큰&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 토큰을 명령어에 그대로 포함하면 PowerShell 명령 기록에 남을 수 있으므로 hf auth login에서 대화식으로 붙여 넣는 방법이 가장 편리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10단계: 텍스트로 이미지 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU가 있는 환경을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image_ai.py를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import torch
from diffusers import AutoPipelineForText2Image

model_id = &quot;stabilityai/sd-turbo&quot;

pipe = AutoPipelineForText2Image.from_pretrained(
    model_id,
    torch_dtype=torch.float16,
    variant=&quot;fp16&quot;,
)

pipe = pipe.to(&quot;cuda&quot;)

prompt = &quot;&quot;&quot;
A quiet Korean traditional village in winter,
warm lights, cinematic composition, highly detailed
&quot;&quot;&quot;

image = pipe(
    prompt=prompt,
    num_inference_steps=2,
    guidance_scale=0.0,
).images[0]

image.save(&quot;generated_image.png&quot;)
print(&quot;generated_image.png 저장 완료&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python image_ai.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 모델은 모델 페이지에 들어가 이용약관에 먼저 동의해야 합니다. 로그인했더라도 약관 동의가 없으면 401 또는 403 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11단계: 이미지 내용을 글로 설명하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요 패키지:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install pillow transformers torch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image_caption.py:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from transformers import pipeline

captioner = pipeline(
    &quot;image-to-text&quot;,
    model=&quot;Salesforce/blip-image-captioning-base&quot;,
    device_map=&quot;auto&quot;,
)

result = captioner(&quot;sample.jpg&quot;)
print(result[0][&quot;generated_text&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python image_caption.py&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12단계: 음성을 텍스트로 변환하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;whisper_ai.py:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from transformers import pipeline

transcriber = pipeline(
    &quot;automatic-speech-recognition&quot;,
    model=&quot;openai/whisper-small&quot;,
    device_map=&quot;auto&quot;,
)

result = transcriber(
    &quot;meeting.mp3&quot;,
    generate_kwargs={
        &quot;language&quot;: &quot;korean&quot;,
        &quot;task&quot;: &quot;transcribe&quot;,
    },
)

print(result[&quot;text&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python whisper_ai.py&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13단계: Claude Code에서 Hugging Face CLI 활용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 Hugging Face 모델 자체가 아닙니다. 두 서비스를 연결하는 방법은 크게 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 A: Claude에게 실행 코드를 작성&amp;middot;수정하게 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 또는 Claude Code에 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Hugging Face의 Qwen/Qwen3-0.6B 모델을 로컬에서 실행하는
Python 프로그램을 작성해 줘.

조건:
- transformers pipeline 사용
- Windows와 VS Code 기준
- 토큰은 환경 변수로 읽기
- 한국어 입력 예제 포함
- 오류가 발생하면 원인을 설명하기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 VS Code 터미널에서 사용한다면 Claude가 프로젝트 파일을 만들고 실행 오류를 수정하도록 할 수도 있습니다. 다만 모델 다운로드와 실제 연산은 여전히 사용자 PC 또는 Hugging Face API에서 수행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 B: Hugging Face MCP 연결하기&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Hugging Face에 로그인합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/settings/mcp&quot;&gt;&lt;span&gt;&lt;span&gt;MCP 설정 페이지&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 Claude Desktop 또는 해당 MCP 클라이언트를 선택합니다.&lt;/li&gt;
&lt;li&gt;페이지가 제공하는 설정을 복사합니다.&lt;/li&gt;
&lt;li&gt;Claude의 MCP 설정에 붙여 넣습니다.&lt;/li&gt;
&lt;li&gt;Claude를 다시 시작합니다.&lt;/li&gt;
&lt;li&gt;다음처럼 요청합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Hugging Face에서 Apache-2.0 라이선스이며
한국어를 지원하는 4B 이하 텍스트 생성 모델을 찾아서 비교해 줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;텍스트를 이미지로 만드는 공개 모델 중
현재 Inference Provider로 시험 가능한 모델을 찾아 줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face 공식 MCP는 Claude Desktop, VS Code, Cursor, Codex 등 MCP 호환 도구에서 모델&amp;middot;데이터셋&amp;middot;Spaces를 탐색할 수 있도록 지원합니다. &lt;a href=&quot;https://huggingface.co/docs/hub/en/agents-mcp&quot;&gt;&lt;span&gt;&lt;span&gt;공식 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14단계: VS Code 또는 Claude에 MCP 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face 공식 MCP 서버를 연결하면 AI 비서가 다음 작업을 수행할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델과 데이터셋 검색&lt;/li&gt;
&lt;li&gt;모델 설명서 확인&lt;/li&gt;
&lt;li&gt;Hugging Face 문서 검색&lt;/li&gt;
&lt;li&gt;Spaces 기반 커뮤니티 도구 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 순서:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Hugging Face에 로그인합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://huggingface.co/settings/mcp&quot;&gt;&lt;span&gt;&lt;span&gt;Hugging Face MCP 설정&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;을 엽니다.&lt;/li&gt;
&lt;li&gt;VS Code 또는 Claude Desktop을 선택합니다.&lt;/li&gt;
&lt;li&gt;화면에 제공되는 설정을 복사합니다.&lt;/li&gt;
&lt;li&gt;해당 프로그램의 MCP 설정에 붙여 넣습니다.&lt;/li&gt;
&lt;li&gt;VS Code 또는 Claude를 다시 시작합니다.&lt;/li&gt;
&lt;li&gt;MCP 도구 목록에서 Hugging Face가 표시되는지 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 설정 형식은 프로그램 버전에 따라 바뀔 수 있으므로 공식 설정 페이지에서 현재 클라이언트용 코드를 복사하는 것이 안전합니다. &lt;a href=&quot;https://huggingface.co/docs/hub/en/agents-mcp&quot;&gt;&lt;span&gt;&lt;span&gt;공식 MCP 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 명령 한 번에 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 텍스트&amp;middot;API 환경:&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;python -m venv .venv
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install --upgrade huggingface_hub transformers accelerate safetensors
pip install torch --index-url https://download.pytorch.org/whl/cpu
hf auth login
hf auth whoami&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지&amp;middot;음성 기능 추가:&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;pip install diffusers pillow sentencepiece protobuf librosa soundfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 검사:&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;python -c &quot;import torch; print('PyTorch:', torch.__version__)&quot;
python -c &quot;import transformers; print('Transformers:', transformers.__version__)&quot;
python -c &quot;from huggingface_hub import whoami; print(whoami())&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 캡처 없이도 메뉴 이름과 명령만으로 진행할 수 있도록 구성했습니다. Hugging Face 화면이 변경되더라도 Settings &amp;rarr; Access Tokens와 공식 MCP 설정 페이지를 기준으로 진행하면 됩니다.&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/15</guid>
      <comments>https://sojsoj.tistory.com/15#entry15comment</comments>
      <pubDate>Wed, 22 Jul 2026 16:51:24 +0900</pubDate>
    </item>
    <item>
      <title>15일차_YOLO 심화</title>
      <link>https://sojsoj.tistory.com/14</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;YOLO 심화&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1f4d78;&quot;&gt;탐지에서 추적&amp;middot;분석&amp;middot;배포까지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기준 모델: yolo11n.pt (작고 빠른 Nano 모델)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;0. 시작 전 꼭 알아둘 것&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;YOLO 모델 이름 읽기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;yolo11n.pt의 n은 Nano를 뜻한다. 일반적으로 n &amp;rarr; s &amp;rarr; m &amp;rarr; l &amp;rarr; x 순서로 모델이 커지고 정확도 잠재력은 높아지지만, 속도와 메모리 부담도 커진다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;모델&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;특징&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;초보자 권장 상황&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;yolo11n.pt&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;가장 작고 빠름&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;CPU&amp;middot;저사양 PC&amp;middot;웹캠 실습&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;yolo11s.pt&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;속도와 정확도의 절충&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;Nano 결과가 부족할 때&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;yolo11m.pt 이상&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;더 무겁고 느릴 수 있음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;GPU가 있고 정확도가 더 중요할 때&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #fff7d6; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7a5a00;&quot;&gt;주의 &lt;/span&gt;&lt;span&gt;큰 모델이 항상 내 환경에서 더 좋은 것은 아니다. 먼저 yolo11n.pt로 전체 코드가 정상 작동하는지 확인한 뒤 모델만 바꾼다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;경로와 들여쓰기: 오늘 가장 많이 만난 오류&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;상대 경로는 현재 실행 폴더를 기준으로 찾는다. FileNotFoundError가 나면 os.path.abspath()로 실제 탐색 위치를 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;while문 안에서 반복할 코드는 모두 4칸 들여쓴다. 반복문 밖의 break는 SyntaxError: 'break' outside loop를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;함수 호출에는 괄호가 필요하다: cap.release()와 cv2.destroyAllWindows().&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;실행 위치가 달라도 안전한 경로 만들기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from pathlib import Path&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;base_dir = Path(__file__).resolve().parent&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;image_path = base_dir / &quot;demo_data&quot; / &quot;image_1.png&quot;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;if not image_path.exists():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; print(&quot;찾고 있는 위치:&quot;, image_path)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; raise SystemExit&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;1. 객체 탐지와 객체 추적&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;구분&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;탐지(Detection)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;추적(Tracking)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;목적&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;현재 프레임의 물체 위치&amp;middot;종류 찾기&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;프레임 사이에서 같은 물체 ID 유지&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;상태 기억&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;없음: 매 프레임 독립&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;있음: 이전 프레임과 연결&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;대표 호출&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;model(frame)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;model.track(frame, persist=True)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;활용&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;사진 분석, 단일 장면&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;동선, 출입, 속도, 체류 분석&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;persist=True는 &amp;lsquo;이번 프레임이 이전 프레임과 이어진다&amp;rsquo;고 추적기에 알려 같은 객체의 ID를 유지하도록 돕는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;기본 추적 코드&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import YOLO&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;import cv2&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = YOLO(&quot;yolo11n.pt&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap = cv2.VideoCapture(0)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = model.track(frame, persist=True, conf=0.6, verbose=False)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; annotated_frame = results[0].plot()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.imshow(&quot;TRACK&quot;, annotated_frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap.release()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #eaf5ea; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #2e7d32;&quot;&gt;초보자 체크 &lt;/span&gt;&lt;span&gt;단일 이미지에는 지속적인 track ID의 의미가 없다. 연속 영상 프레임에서 사용할 때 추적의 장점이 생긴다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #eaf5ea; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;2. from ultralytics import solutions란?&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;solutions는 YOLO의 탐지&amp;middot;추적 결과를 실제 업무 기능으로 묶어 둔 고수준 도구 모음이다. 직접 박스 좌표와 ID를 계산하는 코드 대신, 프레임을 솔루션 객체에 넣고 SolutionResults를 받는 방식으로 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;Solutions 공통 사용 형태&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import solutions&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;tool = solutions.Heatmap(model=&quot;yolo11n.pt&quot;, show=False)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = tool(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;processed_frame = results.plot_im&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;솔루션&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;무엇을 하는가&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;대표 결과&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;Heatmap&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;객체 위치를 누적해 활동 밀도 표시&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;plot_im, total_tracks&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;ObjectCounter&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;선 또는 영역을 통과한 IN/OUT 계산&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;in_count, out_count&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;RegionCounter&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;여러 구역 안의 객체 수 계산&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;region_counts&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;SecurityAlarm&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;탐지 수 조건 충족 시 이메일 알림&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;email_sent&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;ObjectBlurrer&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;탐지된 객체 영역 자동 블러&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;plot_im&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;DistanceCalculation&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;선택한 두 객체 사이 픽셀 거리&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;pixels_distance&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;SpeedEstimator&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;추적 이동량으로 속도 추정&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;speed_dict&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;ObjectCropper&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;탐지 객체를 이미지로 잘라 저장&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;total_crop_objects&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;TrackZone / QueueManager&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;구역 추적&amp;middot;대기열 분석&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;plot_im, counts&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #fff7d6; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7a5a00;&quot;&gt;중요 &lt;/span&gt;&lt;span&gt;일반 YOLO 결과는 results[0].plot()을 많이 쓰지만, Solutions 반환값은 보통 results.plot_im을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;3. 모든 영상 솔루션의 기본 반복문&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;반복해서 재사용할 수 있는 골격&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap = cv2.VideoCapture(video_path)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;if not cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; print(&quot;영상을 열 수 없습니다.&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; raise SystemExit&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; print(&quot;영상이 끝났거나 프레임 읽기에 실패했습니다.&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = solution(frame) # 반드시 while 안쪽&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.imshow(&quot;RESULT&quot;, results.plot_im)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap.release()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;cv2.VideoCapture(0): 기본 웹캠&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;cv2.VideoCapture('input.mp4'): 동영상 파일&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;cv2.VideoCapture(stream_url): CCTV/HLS 스트림. 주소가 종료되거나 차단되면 프레임을 읽지 못할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;4. 히트맵(Heatmap)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;히트맵은 추적 객체의 위치를 시간에 따라 누적하여 자주 등장하거나 오래 머문 영역을 색의 강도로 보여준다. 매장 동선, 진열대 관심도, 체류 구역, 입지 분석에 활용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #f4f6f9; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1f4d78;&quot;&gt;색 해석 &lt;/span&gt;&lt;span&gt;색 의미는 선택한 colormap에 따라 달라진다. 일반적으로 밝고 뜨거운 색이 누적량이 큰 영역이지만, 반드시 사용한 색상표를 기준으로 해석한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;사람만 누적하는 히트맵&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import solutions&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;import cv2&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;video_path = &quot;input.mp4&quot;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap = cv2.VideoCapture(video_path)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;heatmap = solutions.Heatmap(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; classes=[0],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; conf=0.5,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; colormap=cv2.COLORMAP_MAGMA&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = heatmap(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.imshow(&quot;HEATMAP&quot;, results.plot_im)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap.release()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;background-color: #e8eef5; height: 21px;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;인자&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5; height: 21px;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;의미&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5; height: 21px;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;예&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;model&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;사용 모델&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;yolo11n.pt&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;show&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;솔루션 내부 창 표시 여부&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;False이면 직접 imshow&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;classes&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;분석할 COCO 클래스 번호&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;[0]=사람, [2]=자동차&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;conf&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;최소 신뢰도&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;colormap&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;색상 지도&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;MAGMA, JET, HOT, PARULA&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;region&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;관심 선&amp;middot;다각형(선택)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span&gt;&lt;span&gt;[(x1,y1), ...]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;5. 보안 알람(SecurityAlarm)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SecurityAlarm은 탐지 개수가 records 이상이면 주석이 그려진 이미지를 첨부해 이메일을 한 번 전송하는 솔루션이다. 사람&amp;middot;차량 감지, 무단 침입 경보, 야간 감시에 활용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;Gmail 준비&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Google 계정의 2단계 인증을 켠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;앱 비밀번호에서 16자리 비밀번호를 발급한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일반 로그인 비밀번호 대신 앱 비밀번호를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdecec; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9b1c1c;&quot;&gt;보안 원칙 &lt;/span&gt;&lt;span&gt;앱 비밀번호를 .py 파일에 직접 저장하거나 화면 캡처로 공유하지 않는다. 환경 변수로 전달하고, 설치된 ultralytics 소스 파일을 Ctrl+클릭해서 수정하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;실행 전 환경 변수 설정&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# Windows 명령 프롬프트에서 현재 창에만 설정&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;set GMAIL_FROM=sender@gmail.com&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;set GMAIL_TO=receiver@gmail.com&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;set GMAIL_APP_PASSWORD=발급받은16자리앱비밀번호&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;사람 감지 이메일 알람&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import solutions&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;import cv2, os&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap = cv2.VideoCapture(0)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;alarm = solutions.SecurityAlarm(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; classes=[0], # 사람만&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; conf=0.5,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; records=1 # 1명 이상이면 알림&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;alarm.authenticate(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; os.environ[&quot;GMAIL_FROM&quot;],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; os.environ[&quot;GMAIL_APP_PASSWORD&quot;],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; os.environ[&quot;GMAIL_TO&quot;]&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = alarm(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.imshow(&quot;ALARM&quot;, results.plot_im)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; print(&quot;이메일 발송 여부:&quot;, results.email_sent)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cap.release()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #eaf5ea; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #2e7d32;&quot;&gt;알아둘 점 &lt;/span&gt;&lt;span&gt;기본 구현은 한 이벤트에서 이메일을 반복 발송하지 않도록 email_sent 상태를 사용한다. 실제 서비스에서는 재알림 대기시간, 오탐 방지, 로그 저장을 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;6. 개인정보 보호 블러(ObjectBlurrer)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;탐지된 객체의 박스 영역을 자동으로 흐리게 만들어 얼굴&amp;middot;차량 등 민감한 장면의 노출을 줄인다. 단, yolo11n.pt의 COCO 클래스는 &amp;lsquo;얼굴&amp;rsquo; 전용 모델이 아니므로 사람 전체가 블러될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;사람 영역 블러&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;blurrer = solutions.ObjectBlurrer(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; classes=[0],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; blur_ratio=0.5&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = blurrer(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.imshow(&quot;BLUR&quot;, results.plot_im)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;7. 거리 계산(DistanceCalculation)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;영상 창에서 탐지 박스 두 개를 왼쪽 클릭하면 두 객체 중심 사이의 픽셀 거리를 계산한다. 오른쪽 클릭은 선택 초기화다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fff7d6; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7a5a00;&quot;&gt;단위 주의 &lt;/span&gt;&lt;span&gt;pixels_distance는 실제 미터가 아니라 화면상의 픽셀 거리다. 실제 거리로 바꾸려면 카메라 보정, 원근 보정, 기준 길이가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;두 객체 거리와 상태 출력&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;distance = solutions.DistanceCalculation(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=True&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;while cap.isOpened():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; success, frame = cap.read()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if not success:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = distance.process(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; d = results.pixels_distance&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if d and d &amp;gt; 0:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; status = &quot;SAFE&quot; if d &amp;gt;= 150 else &quot;WARNING&quot; if d &amp;gt;= 100 else &quot;DANGER&quot;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; print(f&quot;[거리] {d:.2f}px [상태] {status}&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; break&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f4f6f9; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1f4d78;&quot;&gt;NULL window 오류 &lt;/span&gt;&lt;span&gt;OpenCV 버전 차이로 'Ultralytics Solutions' 창을 못 찾는다면 show=False로 바꾸고 cv2.namedWindow와 cv2.setMouseCallback을 직접 연결하는 우회가 필요할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;8. 카운팅: 선, 영역, 여러 구역&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;ObjectCounter: 선 통과 IN/OUT&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;count_points = [(376, 222), (378, 268)]&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;counter = solutions.ObjectCounter(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; region=count_points&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = counter(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;IN:&quot;, results.in_count, &quot;OUT:&quot;, results.out_count)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;좌표 2개는 선을 만들고, 3개 이상은 다각형 영역을 만든다. 좌표는 실제 처리 프레임의 크기와 일치해야 한다. 프레임을 640&amp;times;480으로 리사이즈했다면 좌표도 그 화면 기준이어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;RegionCounter: 여러 구역 현재 인원&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;딕셔너리의 각 구역 사이에는 쉼표가 필요하다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;region_points = {&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &quot;region-01&quot;: [(391, 198), (395, 270), (484, 278), (481, 196)],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &quot;region-02&quot;: [(113, 306), (116, 388), (185, 384), (170, 311)]&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;}&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;region_counter = solutions.RegionCounter(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; region=region_points,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; conf=0.4&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = region_counter(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(results.region_counts)&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;목적&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;추천 솔루션&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;핵심 결과&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;문을 지나간 누적 출입&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;ObjectCounter&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;in_count / out_count&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;각 구역에 현재 몇 명인지&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;RegionCounter&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;region_counts&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;한 구역에서 추적만&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;TrackZone&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;plot_im / total_tracks&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;9. 속도 추정(SpeedEstimator)&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;추적 객체의 프레임 간 이동량과 meter_per_pixel을 이용해 속도를 추정한다. 카메라 각도와 실제 축척이 맞지 않으면 숫자는 참고값에 그친다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;speed = solutions.SpeedEstimator(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; show=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; classes=[2],&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; line_width=2,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; max_speed=120,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; meter_per_pixel=0.05&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = speed(frame)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(results.speed_dict)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fff7d6; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7a5a00;&quot;&gt;오타 주의 &lt;/span&gt;&lt;span&gt;line_with가 아니라 line_width다. ValueError: ... is not a valid solution argument가 나오면 인자 철자를 먼저 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;10. SAHI 슬라이싱 추론&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SAHI(Slicing Aided Hyper Inference)는 큰 이미지를 작은 조각으로 나누어 각 조각에서 탐지한 뒤 결과를 원본 좌표로 합친다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;멀리 있는 자동차, 드론&amp;middot;위성 이미지의&lt;b&gt; 작은 객체처럼 전체 이미지를 축소할 때 사라지는 대상을 찾는 데 유리하다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;일반 YOLO&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;SAHI&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;전체 이미지를 한 번에 리사이즈해 추론&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;여러 조각으로 나눠 확대된 상태로 추론&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;빠르고 단순&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;더 느리지만 작은 객체 탐지에 유리&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;중복 박스 병합 부담이 적음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;겹침 비율로 경계 객체를 보완한 뒤 결과 병합&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;SAHI 기본 예제&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from sahi import AutoDetectionModel&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from sahi.predict import get_sliced_prediction&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;detection_model = AutoDetectionModel.from_pretrained(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model_type=&quot;ultralytics&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; model_path=&quot;yolo11n.pt&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; confidence_threshold=0.4,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; device=&quot;cpu&quot;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = get_sliced_prediction(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &quot;demo_data/image_1.png&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; detection_model,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; slice_width=256,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; slice_height=256,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; overlap_width_ratio=0.2,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; overlap_height_ratio=0.2&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results.export_visuals(export_dir=&quot;sahi/&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;탐지 수:&quot;, len(results.object_prediction_list))&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #eaf5ea; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #2e7d32;&quot;&gt;변수 저장 &lt;/span&gt;&lt;span&gt;AutoDetectionModel.from_pretrained() 반환값을 detection_model에, get_sliced_prediction() 반환값을 results에 저장해야 NameError가 나지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;11. FastSAM: 이름&amp;middot;포인트로 영역 분할&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSAM은 박스 탐지보다 더 세밀한 마스크 형태로 사물 영역을 분리한다. 전체 분할, 박스, 포인트, 텍스트 프롬프트를 지원한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import FastSAM&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = FastSAM(&quot;FastSAM-s.pt&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# 메모리를 아끼는 전체 분할&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = model(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &quot;image.png&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; imgsz=320,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; retina_masks=False,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; device=&quot;cpu&quot;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# 특정 포인트의 대상만 선택하려면&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# results = model(&quot;image.png&quot;, points=[[170, 480]], labels=[1], imgsz=320)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fff7d6; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #7a5a00;&quot;&gt;텍스트 프롬프트 &lt;/span&gt;&lt;span&gt;texts='white tent'처럼 이름만으로 선택할 수 있지만 CLIP 모델이 추가로 필요해 다운로드&amp;middot;SSL 오류와 메모리 사용 증가가 생길 수 있다. 저사양 PC에서는 포인트 방식이 더 안정적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;12. 성능 확인과 OpenVINO&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;FPS 측정&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;start_time = time.perf_counter()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = model(frame, verbose=False)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model_time = time.perf_counter() - start_time&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;fps = 1 / model_time if model_time &amp;gt; 0 else 0&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;annotated_frame = results[0].plot()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;cv2.putText(&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; annotated_frame,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; f&quot;{fps:.1f} FPS&quot;,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; (10, 30),&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; cv2.FONT_HERSHEY_SIMPLEX,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; 1,&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; (0, 255, 0),&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; 2&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f4f6f9; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1f4d78;&quot;&gt;해석 &lt;/span&gt;&lt;span&gt;이 값은 모델 추론 중심의 순간 FPS다. 카메라 읽기&amp;middot;그리기&amp;middot;화면 표시까지 포함한 전체 처리 FPS와는 다를 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #4f81bd;&quot;&gt;OpenVINO 변환&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;Intel CPU 환경에서는 OpenVINO 형식이 추론 속도를 높일 수 있다.&lt;/b&gt; 변환은 한 번만 하고, 이후 생성된 폴더를 모델로 로드한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;1단계: 변환&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# 최초 1회만 실행&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import YOLO&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = YOLO(&quot;yolo11n.pt&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model.export(format=&quot;openvino&quot;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;2단계: OpenVINO 모델 사용&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# 이후 실행에서는 변환 폴더 로드&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = YOLO(&quot;yolo11n_openvino_model&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;results = model(frame, verbose=False)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #fdecec; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9b1c1c;&quot;&gt;금지 &lt;/span&gt;&lt;span&gt;model.export()를 while 프레임 반복문 안에 넣지 않는다. 매 프레임마다 모델을 변환하는 심각한 오류가 된다.&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;13. Streamlit으로 웹 UI 만들기&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Streamlit은 Python 코드로 업로드 버튼, 슬라이더, 결과 이미지를 갖춘 간단한 웹 앱을 만든다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모델 데모, 수업 결과 공유, 비개발자용 테스트 화면에 적합하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;yolo_streamlit.py&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;import streamlit as st&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import YOLO&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from PIL import Image&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;st.title(&quot;YOLO 객체 탐지&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;@st.cache_resource&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;def load_model():&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; return YOLO(&quot;yolo11n.pt&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = load_model()&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;uploaded = st.file_uploader(&quot;이미지 선택&quot;, type=[&quot;jpg&quot;, &quot;jpeg&quot;, &quot;png&quot;])&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;if uploaded:&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; image = Image.open(uploaded).convert(&quot;RGB&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; results = model(image)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; plotted = results[0].plot()[..., ::-1] # BGR &amp;rarr; RGB&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; st.image(plotted, caption=&quot;탐지 결과&quot;, use_container_width=True)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;실행 명령&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# py39 환경이 활성화된 폴더에서 실행&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;python -m streamlit run yolo_streamlit.py&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음 실행 시 Email 입력 안내가 나오면 빈칸으로 Enter를 눌러도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Local URL: http://localhost:8501을 브라우저에서 연다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;cd yolo_streamlit.py는 잘못된 명령이다. cd는 폴더 이동에만 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;sreamit이 아니라 streamlit이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;14. 자주 발생한 오류 빠른 해결표&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;오류&amp;middot;증상&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;원인&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;고칠 부분&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;break outside loop&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;break가 while 밖에 있음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;처리&amp;middot;imshow&amp;middot;키 입력 코드를 4칸 들여쓰기&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;NameError&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;변수 이름 오타 또는 반환값 미저장&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;path/patn 확인, results = ... 저장&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;FileNotFoundError&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;실행 기준 폴더와 파일 위치 불일치&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;Path(__file__).parent 또는 절대 경로&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;NULL window&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;마우스 콜백 전에 창이 없음/버전 차이&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;show 설정 확인, 필요 시 namedWindow&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;invalid solution argument&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;인자 철자 오류&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;line_with &amp;rarr; line_width 등 공식 이름 확인&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;SSLError NOT_ENOUGH_DATA&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;CLIP 등 추가 모델 다운로드 실패&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;texts 제거&amp;middot;네트워크 확인&amp;middot;포인트 방식&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;프레임 읽기 실패&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;경로&amp;middot;카메라&amp;middot;CCTV 주소 문제&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;cap.isOpened() 먼저 확인&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;결과 이미지가 저장 안 됨&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;cv2.imwrite 누락 또는 잘못된 폴더&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;폴더 생성 후 imwrite 반환값 확인&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;Streamlit 명령을 못 찾음&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;오타 또는 미설치&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;python -m streamlit run 파일.py&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;# 문제를 좁히는 기본 점검&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;현재 작업 폴더:&quot;, os.getcwd())&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;파일 절대 경로:&quot;, os.path.abspath(input_path))&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;파일 존재:&quot;, os.path.exists(input_path))&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(&quot;OpenCV 버전:&quot;, cv2.__version__)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;부록 A. 클래스 번호와 좌표&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;COCO 사전학습 모델에서 자주 쓰는 클래스 번호 예시다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; 모델이나 사용자 데이터셋이 바뀌면 번호도 달라질 수 있으므로 model.names로 확인한다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: left; 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 style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;번호&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;클래스&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e8eef5;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #183b56;&quot;&gt;사용 예&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;person&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[0]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;bicycle&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[1]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;car&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[2]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;motorcycle&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[3]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;bus&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[5]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;7&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;truck&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;classes=[7]&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #5f6b76;&quot;&gt;현재 모델의 클래스 번호 확인&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #f6f8fa; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202124;&quot;&gt;from ultralytics import YOLO&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt; &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;model = YOLO(&quot;yolo11n.pt&quot;)&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #202124;&quot;&gt;print(model.names)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;좌표는 (x, y)이며 왼쪽 위가 (0, 0)이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;x는 오른쪽으로, y는 아래쪽으로 커진다. resize 이후에는 새 프레임 크기를 기준으로 좌표를 다시 잡는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #365f91;&quot;&gt;부록 B. 공식 문서&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Ultralytics Solutions: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/solutions/&quot;&gt;https://docs.ultralytics.com/solutions/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;객체 추적: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/modes/track/&quot;&gt;https://docs.ultralytics.com/modes/track/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Heatmap: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/guides/heatmaps/&quot;&gt;https://docs.ultralytics.com/guides/heatmaps/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Security Alarm: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/guides/security-alarm-system/&quot;&gt;https://docs.ultralytics.com/guides/security-alarm-system/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;SAHI 슬라이싱 추론: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/guides/sahi-tiled-inference/&quot;&gt;https://docs.ultralytics.com/guides/sahi-tiled-inference/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;list-style-type: none; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSAM: &lt;/span&gt;&lt;span style=&quot;color: #2e74b5;&quot;&gt;&lt;a href=&quot;https://docs.ultralytics.com/models/fast-sam/&quot;&gt;https://docs.ultralytics.com/models/fast-sam/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/14</guid>
      <comments>https://sojsoj.tistory.com/14#entry14comment</comments>
      <pubDate>Tue, 21 Jul 2026 16:49:53 +0900</pubDate>
    </item>
    <item>
      <title>14일차_DATA 수집 ~ YOLO 객체 탐지</title>
      <link>https://sojsoj.tistory.com/13</link>
      <description>&lt;h1&gt;오늘의 AI 학습 정리: 데이터 수집부터 라벨링, YOLO 활용까지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 이미지 속 사물을 인식하도록 만들기 위해서는 모델만 실행하는 것이 아니라, 데이터를 수집하고 정답을 표시한 뒤 학습과 검증을 거치는 과정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 과정은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 수집 &amp;rarr; 데이터 증강 &amp;rarr; 라벨링 &amp;rarr; 모델 학습 &amp;rarr; 검증 &amp;rarr; 추론 &amp;rarr; 실시간 활용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. AI 학습용 데이터 수집&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델의 성능은 데이터의 양과 품질에 큰 영향을 받습니다. 같은 물체라도 방향, 거리, 크기, 밝기, 배경이 달라질 수 있으므로 다양한 환경에서 촬영한 이미지가 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 공개 데이터 사이트 이용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 모든 이미지를 촬영하기 어렵다면 공개 데이터 사이트를 이용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hancom-nine.vercel.app/data.html&quot;&gt;&lt;span&gt;&lt;span&gt;Data &amp;mdash; 학습 허브&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AI Hub&lt;/li&gt;
&lt;li&gt;COCO Dataset&lt;/li&gt;
&lt;li&gt;Open Images&lt;/li&gt;
&lt;li&gt;Roboflow Universe&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개 데이터를 사용할 때는 다음 내용을 확인해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상업적 이용이 가능한가?&lt;/li&gt;
&lt;li&gt;데이터를 수정하거나 재배포할 수 있는가?&lt;/li&gt;
&lt;li&gt;출처 표시가 필요한가?&lt;/li&gt;
&lt;li&gt;얼굴이나 차량 번호판 등 개인정보가 포함되어 있는가?&lt;/li&gt;
&lt;li&gt;현재 프로젝트의 학습 목적과 이미지 환경이 비슷한가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 양이 많아도 실제 사용할 환경과 너무 다르면 좋은 성능을 얻기 어렵습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2. 카메라로 직접 수집&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenCV를 사용하면 웹캠으로 사진을 촬영하여 저장할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import cv2
import os
from datetime import datetime

save_dir = &quot;./captured_images&quot;
os.makedirs(save_dir, exist_ok=True)

cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print(&quot;카메라를 열 수 없습니다.&quot;)
    raise SystemExit

success, frame = cap.read()

if success:
    timestamp = datetime.now().strftime(&quot;%Y%m%d_%H%M%S&quot;)
    file_path = os.path.join(
        save_dir,
        f&quot;result_{timestamp}.jpg&quot;
    )

    cv2.imwrite(file_path, frame)
    print(f&quot;사진 저장 완료: {file_path}&quot;)
else:
    print(&quot;카메라에서 사진을 읽지 못했습니다.&quot;)

cap.release()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VideoCapture(0)의 0은 기본 카메라를 의미합니다. 외장 카메라를 사용한다면 환경에 따라 1 또는 2로 변경해야 할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 데이터 증강&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 증강(Data Augmentation)은 기존 이미지를 회전하거나 밝기를 변경하는 방식으로 데이터의 다양성을 늘리는 기법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 한 장으로 여러 환경을 간접적으로 만들어 모델이 다양한 상황에 대응하도록 도울 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from PIL import Image, ImageEnhance, ImageOps

img = Image.open(&quot;input.jpg&quot;)

# 90도 회전
rotated = img.rotate(90, expand=True)
rotated.save(&quot;rotated.jpg&quot;)

# 밝기를 50%로 조절
dark = ImageEnhance.Brightness(img).enhance(0.5)
dark.save(&quot;dark.jpg&quot;)

# 좌우 반전
mirrored = ImageOps.mirror(img)
mirrored.save(&quot;mirrored.jpg&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 증강 방법은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회전&lt;/li&gt;
&lt;li&gt;밝기 변경&lt;/li&gt;
&lt;li&gt;좌우 반전&lt;/li&gt;
&lt;li&gt;크기 변경&lt;/li&gt;
&lt;li&gt;일부 영역 자르기&lt;/li&gt;
&lt;li&gt;노이즈 추가&lt;/li&gt;
&lt;li&gt;색상 변경&lt;/li&gt;
&lt;li&gt;원근 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 탐지 데이터 증강 시 주의사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 분류는 이미지 전체의 종류를 판단하기 때문에 사진만 변형해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 객체 탐지와 세그멘테이션에서는 이미지가 회전하거나 반전될 때 객체의 박스와 외곽선 좌표도 함께 변환해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지만 회전하고 라벨 좌표를 그대로 두면 객체의 실제 위치와 정답 위치가 달라져 잘못된 데이터를 학습하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 원본 이미지를 학습&amp;middot;검증 데이터로 먼저 분리한 뒤 증강하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 원본에서 만든 이미지가 학습 세트와 검증 세트에 동시에 들어가면 실제보다 성능이 높게 측정될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ultralytics YOLO는 학습 과정에서 회전, 밝기, 좌우 반전, Mosaic 등의 데이터 증강 옵션을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 설정은 &lt;a href=&quot;https://docs.ultralytics.com/modes/train&quot;&gt;&lt;span&gt;&lt;span&gt;Ultralytics 학습 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 데이터 라벨링이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 라벨링은 AI 모델에 정답을 알려주는 작업입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 탐지에서는 이미지 속 객체를 사각형 박스로 감싸고 다음 정보를 기록합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 종류&lt;/li&gt;
&lt;li&gt;객체 중심의 위치&lt;/li&gt;
&lt;li&gt;객체 박스의 너비&lt;/li&gt;
&lt;li&gt;객체 박스의 높이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 식탁과 꽃병을 학습한다면 다음과 같이 클래스를 정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;names:
  0: dining_table
  1: vase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 학습하는 모델에서는 클래스 번호를 사용자가 정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면&lt;span style=&quot;color: #ee2323;&quot;&gt; 주의 ! COCO 데이터로 사전 학습된 YOLO11 모델은 클래스 번호가 이미 정해져 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;60 = dining table
75 = vase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 모델의 전체 클래스 번호는 다음 코드로 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

print(model.names)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 라벨링 도구&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1. LabelImg&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LabelImg는 컴퓨터에 설치해서 사용하는 라벨링 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷 없이 사용 가능&lt;/li&gt;
&lt;li&gt;사용 방법이 비교적 간단함&lt;/li&gt;
&lt;li&gt;이미지가 외부 서버에 업로드되지 않음&lt;/li&gt;
&lt;li&gt;바운딩 박스 라벨링에 적합함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 사람이 동시에 작업하기 어려움&lt;/li&gt;
&lt;li&gt;데이터 버전 관리 기능이 부족함&lt;/li&gt;
&lt;li&gt;주로 객체 탐지용 박스 라벨링에 적합함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2. Roboflow&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Roboflow는 웹 브라우저에서 데이터를 업로드하고 라벨링할 수 있는 서비스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;별도의 프로그램 설치 없이 사용 가능&lt;/li&gt;
&lt;li&gt;팀 단위 협업 가능&lt;/li&gt;
&lt;li&gt;학습&amp;middot;검증&amp;middot;테스트 데이터 분리 지원&lt;/li&gt;
&lt;li&gt;데이터 증강 기능 지원&lt;/li&gt;
&lt;li&gt;YOLO 형식으로 내보내기 가능&lt;/li&gt;
&lt;li&gt;모델을 이용한 자동 라벨링 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷 연결 필요&lt;/li&gt;
&lt;li&gt;서비스 요금 및 공개 범위 확인 필요&lt;/li&gt;
&lt;li&gt;개인정보가 포함된 이미지 업로드 시 주의 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. YOLO를 이용한 자동 라벨링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO를 이용해서도 이미지에 라벨을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 말하면 YOLO가 예측한 결과를 정답 후보로 저장하는 것으로, 이를 다음과 같이 부릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 라벨링&lt;/li&gt;
&lt;li&gt;사전 라벨링(Pre-labeling)&lt;/li&gt;
&lt;li&gt;의사 라벨링(Pseudo-labeling)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1. 폴더 속 이미지 &lt;span style=&quot;color: #666666;&quot;&gt;자동 라벨링&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

model(
    &quot;images&quot;,
    conf=0.5,
    save=True,
    save_txt=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 옵션의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;save=True: 박스가 그려진 결과 이미지 저장&lt;/li&gt;
&lt;li&gt;save_txt=True: YOLO 형식의 라벨 파일 저장&lt;/li&gt;
&lt;li&gt;conf=0.5: 신뢰도가 50% 이상인 결과만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 일반적으로 다음 폴더에 저장됩니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;runs/
└─ detect/
   └─ predict/
      ├─ image01.jpg
      ├─ image02.jpg
      └─ labels/
         ├─ image01.txt
         └─ image02.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 탐지 라벨 파일에는 객체 하나당 한 줄씩 다음 값이 저장됩니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class_id x_center y_center width height&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;0 0.512 0.438 0.243 0.572&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌표와 크기는 픽셀이 아니라 이미지 크기를 기준으로 0~1 사이 값으로 정규화됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2. 원하는 객체만 자동 라벨링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식탁과 꽃병만 라벨링하려면 classes 옵션을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

model(
    &quot;images&quot;,
    classes=[60, 75],
    conf=0.4,
    save=True,
    save_txt=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;60 = dining table
75 = vase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번호를 외우지 않고 이름으로 찾을 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

target_names = [
    &quot;dining table&quot;,
    &quot;vase&quot;
]

target_ids = [
    class_id
    for class_id, class_name in model.names.items()
    if class_name in target_names
]

print(target_ids)

model(
    &quot;images&quot;,
    classes=target_ids,
    conf=0.4,
    save=True,
    save_txt=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-3. 세그멘테이션 자동 라벨링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사각형 박스가 아니라 객체의 실제 외곽선이 필요하다면 세그멘테이션 모델을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n-seg.pt&quot;)

model(
    &quot;images&quot;,
    classes=[60, 75],
    conf=0.4,
    save=True,
    save_txt=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동 라벨링의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO가 만든 라벨은 모델의 예측 결과이므로 완벽한 정답이 아닙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 객체를 놓칠 수 있음&lt;/li&gt;
&lt;li&gt;다른 물체를 잘못 탐지할 수 있음&lt;/li&gt;
&lt;li&gt;객체 박스의 크기와 위치가 부정확할 수 있음&lt;/li&gt;
&lt;li&gt;겹치거나 일부만 보이는 객체를 놓칠 수 있음&lt;/li&gt;
&lt;li&gt;기본 모델이 배우지 않은 물체는 인식하기 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 방식이 가장 안전합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO 자동 라벨 생성 &amp;rarr; LabelImg 또는 Roboflow에서 검수 &amp;rarr; 누락된 객체 추가 &amp;rarr; 잘못된 박스 수정 &amp;rarr; 학습 데이터로 사용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 제품이나 부품처럼 기본 YOLO가 모르는 객체는 먼저 일부 이미지를 사람이 직접 라벨링해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 1차 모델을 학습하고, 그 모델로 나머지 이미지에 자동 라벨을 생성하면 작업 시간을 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. YOLO란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO는 &amp;ldquo;You Only Look Once&amp;rdquo;의 약자로, 이미지나 영상에서 여러 객체의 종류와 위치를 빠르게 찾아내는 컴퓨터 비전 모델입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 한 장뿐만 아니라 다음과 같은 입력을 처리할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지&lt;/li&gt;
&lt;li&gt;이미지 폴더&lt;/li&gt;
&lt;li&gt;동영상&lt;/li&gt;
&lt;li&gt;웹캠&lt;/li&gt;
&lt;li&gt;CCTV&lt;/li&gt;
&lt;li&gt;HTTP 영상 스트림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO11은 객체 탐지뿐 아니라 이미지 분류, 인스턴스 분할, 자세 추정, 회전 객체 탐지 등을 지원합니다. &lt;a href=&quot;https://docs.ultralytics.com/models/yolo11&quot;&gt;&lt;span&gt;&lt;span&gt;YOLO11 공식 모델 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;모델기능결과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;yolo11n.pt&lt;/td&gt;
&lt;td&gt;객체 탐지&lt;/td&gt;
&lt;td&gt;객체 이름과 사각형 박스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yolo11n-seg.pt&lt;/td&gt;
&lt;td&gt;인스턴스 분할&lt;/td&gt;
&lt;td&gt;객체의 실제 영역&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yolo11n-pose.pt&lt;/td&gt;
&lt;td&gt;자세 추정&lt;/td&gt;
&lt;td&gt;사람의 관절 위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yolo11n-cls.pt&lt;/td&gt;
&lt;td&gt;이미지 분류&lt;/td&gt;
&lt;td&gt;이미지 전체의 종류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yolo11n-obb.pt&lt;/td&gt;
&lt;td&gt;회전 객체 탐지&lt;/td&gt;
&lt;td&gt;방향이 포함된 박스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델명에 사용되는 n, s, m, l, x는 모델의 크기를 의미합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n: 가장 작고 빠른 모델&lt;/li&gt;
&lt;li&gt;s: 작지만 n보다 정확도가 높은 모델&lt;/li&gt;
&lt;li&gt;m: 속도와 정확도의 중간&lt;/li&gt;
&lt;li&gt;l, x: 더 크고 많은 연산이 필요한 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 CPU 또는 웹캠 실습을 할 때는 yolo11n.pt부터 시작하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. YOLO 설치와 기본 객체 탐지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 Ultralytics 패키지를 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install ultralytics&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 객체 탐지 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

results = model(
    &quot;input.jpg&quot;,
    conf=0.5,
    save=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;save=True를 사용하면 결과가 일반적으로 다음 위치에 저장됩니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;runs/detect/predict&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 코드를 반복 실행하면 기존 결과를 덮어쓰지 않고 새 폴더가 만들어집니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;predict
predict2
predict3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업별 기본 저장 폴더도 다릅니다.&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;객체 탐지       &amp;rarr; runs/detect
세그멘테이션    &amp;rarr; runs/segment
자세 추정       &amp;rarr; runs/pose
이미지 분류     &amp;rarr; runs/classify
객체 추적       &amp;rarr; runs/track 또는 작업별 결과 폴더&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 원하는 객체만 탐지하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;classes 옵션을 사용하면 특정 클래스만 탐지할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n-seg.pt&quot;)

model(
    &quot;input_seg.jpg&quot;,
    classes=[60, 75],
    conf=0.5,
    save=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 식탁과 꽃병만 탐지하고 분할합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 학습한 모델에서는 클래스 번호가 data.yaml의 순서에 따라 달라집니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;names:
  0: vase
  1: dining_table&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 정의하고 학습했다면 다음과 같이 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;model(
    &quot;input.jpg&quot;,
    classes=[0, 1],
    save=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 주요 파라미터&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;results = model(
    &quot;input.jpg&quot;,
    conf=0.5,
    imgsz=640,
    max_det=5,
    classes=[0, 2],
    save=True,
    project=&quot;results&quot;,
    name=&quot;my_prediction&quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;파라미터설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;conf=0.5&lt;/td&gt;
&lt;td&gt;신뢰도 50% 이상만 탐지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;imgsz=640&lt;/td&gt;
&lt;td&gt;모델 입력 이미지 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max_det=5&lt;/td&gt;
&lt;td&gt;한 이미지의 최대 탐지 개수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;classes=[0, 2]&lt;/td&gt;
&lt;td&gt;지정한 클래스만 탐지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;save=True&lt;/td&gt;
&lt;td&gt;결과 이미지 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;save_txt=True&lt;/td&gt;
&lt;td&gt;탐지 결과를 라벨 파일로 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;project=&quot;results&quot;&lt;/td&gt;
&lt;td&gt;결과를 저장할 상위 폴더&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;name=&quot;test1&quot;&lt;/td&gt;
&lt;td&gt;결과 폴더 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;device=0&lt;/td&gt;
&lt;td&gt;첫 번째 GPU 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;device=&quot;cpu&quot;&lt;/td&gt;
&lt;td&gt;CPU 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;verbose=False&lt;/td&gt;
&lt;td&gt;반복 출력 메시지 줄이기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conf가 너무 높으면 실제 객체를 놓칠 수 있고, 너무 낮으면 잘못 탐지하는 객체가 늘어날 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 0.25~0.5 정도에서 비교해보는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 추론 결과 직접 저장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO의 plot()을 실행하면 탐지 박스가 그려진 이미지 배열을 얻을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from ultralytics import YOLO
import cv2

model = YOLO(&quot;yolo11n.pt&quot;)
results = model(&quot;input.jpg&quot;)

result_image = results[0].plot()

cv2.imwrite(&quot;result.jpg&quot;, result_image)

print(&quot;결과 이미지가 저장되었습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 plot 뒤에 괄호가 없으면 함수가 실행되지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;results[0].plot    # 잘못된 사용
results[0].plot()  # 올바른 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론 결과에는 작업에 따라 다음 정보가 들어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;results[0].boxes       # 객체 탐지 박스
results[0].masks       # 세그멘테이션 마스크
results[0].keypoints   # 자세 추정 관절 좌표
results[0].probs       # 이미지 분류 확률&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 결과 구조는 &lt;a href=&quot;https://docs.ultralytics.com/modes/predict&quot;&gt;&lt;span&gt;&lt;span&gt;Ultralytics 추론 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 웹캠 실시간 객체 탐지&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;from ultralytics import YOLO
import cv2

model = YOLO(&quot;yolo11n.pt&quot;)
cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print(&quot;카메라를 열 수 없습니다.&quot;)
    raise SystemExit

while cap.isOpened():
    success, frame = cap.read()

    if not success:
        print(&quot;프레임 읽기 실패&quot;)
        break

    results = model(
        frame,
        conf=0.5,
        verbose=False
    )

    annotated_frame = results[0].plot()

    cv2.imshow(
        &quot;YOLO Webcam&quot;,
        annotated_frame
    )

    if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):
        print(&quot;q키를 눌러 종료합니다.&quot;)
        break

cap.release()
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 자주 틀리는 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;cap.isOpened()  # isOpend()가 아님
ord(&quot;q&quot;)        # waitKey 결과와 비교할 때 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;break는 반드시 while 또는 for 반복문 내부에서 사용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. HTTP CCTV 스트림 연결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹캠 대신 HTTP 영상 스트림 주소를 전달할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;from ultralytics import YOLO
import cv2

stream_url = &quot;http://example.com/live/playlist.m3u8&quot;

cap = cv2.VideoCapture(stream_url)
model = YOLO(&quot;yolo11n.pt&quot;)

if not cap.isOpened():
    print(&quot;스트림을 열 수 없습니다.&quot;)
    raise SystemExit

while cap.isOpened():
    success, frame = cap.read()

    if not success:
        print(&quot;스트림 프레임 읽기 실패&quot;)
        break

    results = model(
        frame,
        conf=0.5,
        verbose=False
    )

    annotated_frame = results[0].plot()

    cv2.imshow(
        &quot;CCTV YOLO&quot;,
        annotated_frame
    )

    if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):
        break

cap.release()
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 스트림은 다음 원인으로 열리지 않을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스트림 주소 만료&lt;/li&gt;
&lt;li&gt;서버 접속 제한&lt;/li&gt;
&lt;li&gt;네트워크 연결 문제&lt;/li&gt;
&lt;li&gt;OpenCV가 지원하지 않는 코덱&lt;/li&gt;
&lt;li&gt;동시 접속자 제한&lt;/li&gt;
&lt;li&gt;인증이 필요한 스트림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개 CCTV 영상을 사용할 때는 해당 서비스의 이용 조건과 개인정보 관련 규정도 확인해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 영상 속 객체 추적하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 탐지는 각 프레임에서 객체를 새로 찾습니다. 객체 추적은 같은 객체에 ID를 부여해 영상이 진행되어도 계속 구분하는 기능입니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;from ultralytics import YOLO
import cv2

model = YOLO(&quot;yolo11n.pt&quot;)
cap = cv2.VideoCapture(0)

while cap.isOpened():
    success, frame = cap.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        conf=0.5,
        verbose=False
    )

    annotated_frame = results[0].plot()

    cv2.imshow(
        &quot;YOLO Tracking&quot;,
        annotated_frame
    )

    if cv2.waitKey(1) &amp;amp; 0xFF == ord(&quot;q&quot;):
        break

cap.release()
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;persist=True는 이전 프레임의 추적 정보를 다음 프레임에서도 이어서 사용하라는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 추적은 다음 분야에 활용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사람 수 계산&lt;/li&gt;
&lt;li&gt;차량 이동 분석&lt;/li&gt;
&lt;li&gt;위험 구역 진입 감지&lt;/li&gt;
&lt;li&gt;물류 제품 이동 추적&lt;/li&gt;
&lt;li&gt;특정 객체의 이동 경로 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 &lt;a href=&quot;https://docs.ultralytics.com/modes/track&quot;&gt;&lt;span&gt;&lt;span&gt;Ultralytics 객체 추적 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 나만의 객체 탐지 모델 학습하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 YOLO11 탐지 모델은 COCO 데이터셋의 80개 클래스를 학습한 모델입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 목록에 없는 제품, 부품, 불량 유형, 특정 도구 등을 탐지하려면 직접 데이터를 수집하고 학습해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 폴더 구조&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;dataset/
├─ images/
│  ├─ train/
│  └─ val/
├─ labels/
│  ├─ train/
│  └─ val/
└─ data.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;data.yaml 작성&lt;/h3&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;path: ./dataset

train: images/train
val: images/val

names:
  0: dining_table
  1: vase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO 객체 탐지 라벨은 이미지마다 하나의 .txt 파일로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class_id x_center y_center width height&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 번호는 0부터 시작하고 좌표는 0~1 사이의 값으로 정규화합니다. 자세한 구조는 &lt;a href=&quot;https://docs.ultralytics.com/datasets/detect&quot;&gt;&lt;span&gt;&lt;span&gt;Ultralytics 객체 탐지 데이터 형식&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모델 학습&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(&quot;yolo11n.pt&quot;)

model.train(
    data=&quot;data.yaml&quot;,
    epochs=100,
    imgsz=640,
    batch=16
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 결과는 일반적으로 다음 폴더에 저장됩니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;runs/detect/train&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 성능이 좋았던 모델은 다음 파일입니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;runs/detect/train/weights/best.pt&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습한 모델로 추론&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from ultralytics import YOLO

model = YOLO(
    &quot;runs/detect/train/weights/best.pt&quot;
)

model(
    &quot;test.jpg&quot;,
    conf=0.5,
    save=True
)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. 효율적인 자동 라벨링 학습 순서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 만든 객체를 학습할 때는 다음 순서가 효율적입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;여러 환경에서 이미지를 수집한다.&lt;/li&gt;
&lt;li&gt;일부 이미지에 사람이 직접 라벨링한다.&lt;/li&gt;
&lt;li&gt;직접 만든 라벨로 1차 YOLO 모델을 학습한다.&lt;/li&gt;
&lt;li&gt;1차 모델로 나머지 이미지에 자동 라벨을 생성한다.&lt;/li&gt;
&lt;li&gt;LabelImg 또는 Roboflow에서 자동 라벨을 검수한다.&lt;/li&gt;
&lt;li&gt;잘못된 박스를 수정하고 누락된 객체를 추가한다.&lt;/li&gt;
&lt;li&gt;수정된 전체 데이터로 최종 모델을 다시 학습한다.&lt;/li&gt;
&lt;li&gt;학습에 사용하지 않은 테스트 이미지로 성능을 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하면 사람이 모든 이미지에 처음부터 박스를 그리는 시간을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 데이터 수집 &amp;rarr; 정확한 라벨링 &amp;rarr; 모델 학습 &amp;rarr; 결과 검증 &amp;rarr; 실제 환경 적용&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;오늘 학습의 핵심 :&amp;nbsp; AI 프로젝트 전체 흐름을 이해&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&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;li&gt;학습&amp;middot;검증 데이터를 분리한다.&lt;/li&gt;
&lt;li&gt;작업에 맞는 YOLO 모델을 선택한다.&lt;/li&gt;
&lt;li&gt;파라미터를 조절하며 추론 결과를 확인한다.&lt;/li&gt;
&lt;li&gt;이미지, 웹캠, CCTV 및 영상 추적으로 확장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLO는 다음과 같은 기능을 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 분류&lt;/li&gt;
&lt;li&gt;객체 탐지&lt;/li&gt;
&lt;li&gt;인스턴스 분할&lt;/li&gt;
&lt;li&gt;자세 추정&lt;/li&gt;
&lt;li&gt;객체 추적&lt;/li&gt;
&lt;li&gt;이미지&amp;middot;동영상 추론&lt;/li&gt;
&lt;li&gt;결과 이미지와 라벨 저장&lt;/li&gt;
&lt;li&gt;원하는 클래스만 선택&lt;/li&gt;
&lt;li&gt;파라미터 조절&lt;/li&gt;
&lt;li&gt;웹캠 및 CCTV 스트림 처리&lt;/li&gt;
&lt;li&gt;자동 라벨링&lt;/li&gt;
&lt;li&gt;사용자 데이터 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/13</guid>
      <comments>https://sojsoj.tistory.com/13#entry13comment</comments>
      <pubDate>Mon, 20 Jul 2026 16:43:26 +0900</pubDate>
    </item>
    <item>
      <title>13일차_ Anaconda / Python문법</title>
      <link>https://sojsoj.tistory.com/12</link>
      <description>&lt;h1 style=&quot;background-color: #000000; color: #e6edf3; text-align: center;&quot;&gt;Anaconda&lt;/h1&gt;
&lt;h1&gt;오늘의 학습 정리 &amp;mdash; 아나콘다 설치와 활용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://hancom-nine.vercel.app/python.html&quot;&gt;&lt;span&gt;&lt;span&gt;Python 학습 사이트&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 아나콘다란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아나콘다는 파이썬과 여러 패키지, 개발 도구를 편리하게 설치하고 관리할 수 있게 해주는 프로그램이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;b&gt;가상환경&lt;/b&gt;을 만들어 프로젝트별로 파이썬 버전과 패키지를 따로 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;py39: Python 3.9 학습 환경&lt;/li&gt;
&lt;li&gt;cv_env: OpenCV 실습 환경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 환경은 서로 독립되어 있어 한 환경에 설치한 패키지가 다른 환경에 영향을 주지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 아나콘다 설치&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anaconda.com/download&quot;&gt;&lt;span&gt;&lt;span&gt;아나콘다 공식 다운로드 페이지&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;로 이동한다.&lt;/li&gt;
&lt;li&gt;Windows용 설치 프로그램을 내려받는다.&lt;/li&gt;
&lt;li&gt;설치 프로그램을 실행한다.&lt;/li&gt;
&lt;li&gt;설치 방식은 Just Me를 선택한다.&lt;/li&gt;
&lt;li&gt;특별한 이유가 없다면 기본 설정으로 설치한다.&lt;/li&gt;
&lt;li&gt;설치가 끝나면 Windows 검색창에서 &lt;b&gt;Anaconda Prompt&lt;/b&gt;를 찾아 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아나콘다 공식 설명에서도 Windows 설치 시 Just Me를 권장하며, PATH 옵션은 일반적으로 선택하지 않는 것을 권장한다. &lt;a href=&quot;https://www.anaconda.com/docs/getting-started/anaconda/install/windows-gui-install&quot;&gt;&lt;span&gt;&lt;span&gt;Windows 설치 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 정상적으로 완료되면 Anaconda Prompt 앞부분에 다음과 같이 표시된다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;(base) C:\Users\사용자이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;base는 아나콘다의 기본 환경이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 가상환경 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.9를 사용하는 py39 환경을 만들려면 다음 명령어를 입력한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;conda create -n py39 python=3.9&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어의 의미:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;conda create: 새로운 환경 생성&lt;/li&gt;
&lt;li&gt;-n py39: 환경 이름을 py39로 지정&lt;/li&gt;
&lt;li&gt;python=3.9: Python 3.9 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 도중 다음 질문이 나오면 y를 입력하고 Enter를 누른다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Proceed ([y]/n)?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 이름은 원하는 이름으로 정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;conda create -n cv_env python=3.9&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 가상환경 활성화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만든 환경을 사용하려면 먼저 활성화해야 한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate py39&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cv_env라는 환경을 사용한다면:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate cv_env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활성화되면 프롬프트 앞에 현재 환경 이름이 나타난다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;(py39) C:\Users\Har27&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 다음과 같이 이해하면 된다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda activate 환경이름&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;py39와 cv_env를 동시에 입력하는 것이 아니라, 내가 사용할 환경 이름 하나를 입력한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 가상환경 목록 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 컴퓨터에 만들어진 환경을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;conda env list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;conda info --envs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;base    C:\Users\Har27\anaconda3
py39 *  C:\Users\Har27\anaconda3\envs\py39&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 * 표시는 &lt;b&gt;현재 활성화된 환경&lt;/b&gt;을 뜻한다. 제공한 캡처에서는 py39 옆에 *가 있으므로 py39 환경이 정상적으로 활성화된 상태이다. &lt;a href=&quot;https://docs.conda.io/en/latest/user-guide/tasks/manage-environments.html&quot;&gt;&lt;span&gt;&lt;span&gt;Conda 환경 관리 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 패키지 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경을 활성화한 뒤 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;큰 그림 글씨 출력&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install pyfiglet&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;터미널 글자에 색상 적용&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install termcolor&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹캠 및 영상 처리&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install opencv-python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 순서:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;conda activate py39
pip install pyfiglet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 pyfiglet은 현재 활성화된 py39 환경에 설치된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 설치된 패키지 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 환경에 설치된 패키지 목록을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;pip list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 패키지가 설치되었는지 확인하려면:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;pip show pyfiglet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공한 캡처에서는 다음 내용이 확인된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Successfully installed pyfiglet-1.0.4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 pyfiglet 설치가 정상적으로 완료된 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 설치한 패키지 활용하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;pyfiglet으로 큰 글씨 출력&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from pyfiglet import figlet_format

print(figlet_format(&quot;HAPPY&quot;, font=&quot;slant&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;글꼴과 색상을 함께 적용&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from pyfiglet import figlet_format
from termcolor import colored

big_text = lambda text, font=&quot;standard&quot;, color=&quot;blue&quot;: colored(
    figlet_format(text, font=font),
    color
)

print(big_text(&quot;HAPPY&quot;, &quot;slant&quot;, &quot;magenta&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 입력값의 의미:&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;big_text(&quot;HAPPY&quot;, &quot;slant&quot;, &quot;magenta&quot;)
#         글자      글꼴       색상&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;termcolor에는 &quot;pink&quot; 색상이 없으므로 비슷한 &quot;magenta&quot; 또는 &quot;light_magenta&quot;를 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 가상환경 종료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 끝나면 현재 환경을 비활성화한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;conda deactivate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 전:&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;(py39) C:\Users\Har27&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후:&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;(base) C:\Users\Har27&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;py39 표시가 사라졌다면 환경이 종료된 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 명령어 요약&lt;/h2&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 가상환경 생성
conda create -n py39 python=3.9

# 가상환경 활성화
conda activate py39

# 가상환경 목록 확인
conda env list

# 패키지 설치
pip install pyfiglet
pip install termcolor
pip install opencv-python

# 설치된 패키지 확인
pip list

# 현재 환경 종료
conda deactivate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 작업 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Anaconda Prompt 실행
&amp;rarr; 가상환경 생성
&amp;rarr; 가상환경 활성화
&amp;rarr; 필요한 패키지 설치
&amp;rarr; 파이썬 코드 실행
&amp;rarr; 가상환경 종료&lt;/code&gt;&lt;/pre&gt;
&lt;h1 style=&quot;background-color: #000000; color: #e6edf3; text-align: center;&quot;&gt;Python&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Python 변수 타입과 클래스, 정말 쉽게 이해하기&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 값을 가리키는 이름표이고, 클래스는 비슷한 물건을 여러 개 만들기 위한 설계도입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;1. 변수란 무엇일까?&lt;/h1&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;다음은 파이선에서 변수를 지정하는 법입니다. 숫자에는 &quot;&quot; 를 붇이지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 다음과 같은 뜻입니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;name ──&amp;rarr; &quot;민수&quot;
age  ──&amp;rarr; 20&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name이라는 이름표가 &quot;민수&quot;라는 값을 가리킵니다.&lt;/li&gt;
&lt;li&gt;age라는 이름표가 20이라는 값을 가리킵니다.&lt;/li&gt;
&lt;li&gt;=는 오른쪽 값을 왼쪽 이름에 저장한다는 뜻입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(name)
print(age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수
20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;2. 변수 타입이란?&lt;/h1&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;사람에게도 이름, 나이, 키처럼 서로 다른 정보가 있듯이 Python의 값에도 여러 종류가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;민수&quot;       # 글자
age = 20            # 정수
height = 175.5      # 소수
is_student = True   # 참 또는 거짓&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서는 하나의 변수에 다른 종류의 값을 다시 넣을 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;value = 10
value = &quot;안녕하세요&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정확히 말하면 변수 자체에 고정된 타입이 있는 것이 아니라, &lt;b&gt;변수가 현재 가리키는 값에 타입이 있다&lt;/b&gt;고 이해하면 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 공식 입문 문서에서도 숫자, 문자열, 리스트 등을 서로 다른 값의 종류로 설명합니다. &lt;a href=&quot;https://docs.python.org/ko/3/tutorial/introduction.html&quot;&gt;&lt;span&gt;&lt;span&gt;Python 공식 입문 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 사용하는 타입&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;타입의미예
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;소수점이 없는 정수&lt;/td&gt;
&lt;td&gt;10, -3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;소수점이 있는 실수&lt;/td&gt;
&lt;td&gt;3.14, 175.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;글자와 문장&lt;/td&gt;
&lt;td&gt;&quot;Python&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;참 또는 거짓&lt;/td&gt;
&lt;td&gt;True, False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;td&gt;순서가 있고 수정 가능한 목록&lt;/td&gt;
&lt;td&gt;[10, 20, 30]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tuple&lt;/td&gt;
&lt;td&gt;수정할 수 없는 목록&lt;/td&gt;
&lt;td&gt;(10, 20)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;이름표와 값을 짝지어 저장&lt;/td&gt;
&lt;td&gt;{&quot;name&quot;: &quot;민수&quot;}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set&lt;/td&gt;
&lt;td&gt;중복을 허용하지 않는 값의 모음&lt;/td&gt;
&lt;td&gt;{1, 2, 3}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NoneType&lt;/td&gt;
&lt;td&gt;값이 없는 상태&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-1. int: 정수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소수점이 없는 숫자입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;age = 20
score = 95

print(age + 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;21&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 나이, 개수, 점수처럼 소수점이 필요 없는 값을 저장합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-2. float: 실수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소수점이 있는 숫자입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;height = 175.5
temperature = 23.7&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(temperature + 1.5)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;25.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키, 몸무게, 온도, 비율 등에 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-3. str: 문자열&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글자나 문장을 나타냅니다. 문자열은 따옴표로 감쌉니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
message = &quot;안녕하세요&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열끼리는 +를 사용해 연결할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(name + &quot;님, 안녕하세요!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;erlang-repl&quot;&gt;&lt;code&gt;민수님, 안녕하세요!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 두 값은 겉보기에는 비슷하지만 타입이 다릅니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;number1 = 10
number2 = &quot;10&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10은 계산할 수 있는 정수입니다.&lt;/li&gt;
&lt;li&gt;&quot;10&quot;은 글자로 저장된 문자열입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;print(10 + 5)       # 15
print(&quot;10&quot; + &quot;5&quot;)   # 105&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-4. bool: 참과 거짓&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 맞는지 틀린지를 나타냅니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;is_student = True
is_raining = False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 조건문과 함께 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;if is_raining:
    print(&quot;우산을 챙기세요.&quot;)
else:
    print(&quot;우산이 필요 없습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;True와 False의 첫 글자는 반드시 대문자로 작성합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-5. list: 여러 값을 담는 목록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 여러 값을 순서대로 보관합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python의 순서는 1이 아니라 0부터 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;값       사과   바나나   딸기
순서      0      1      2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(fruits[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사과&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에는 새로운 값을 추가할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits.append(&quot;포도&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['사과', '바나나', '딸기', '포도']&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-6. dict: 이름표와 값을 함께 저장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 데이터를 키: 값 형태로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;person = {
    &quot;name&quot;: &quot;민수&quot;,
    &quot;age&quot;: 20,
    &quot;job&quot;: &quot;학생&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 이용해 원하는 값을 꺼낼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(person[&quot;name&quot;])
print(person[&quot;age&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수
20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 이름, 나이, 직업처럼 &lt;b&gt;각 값의 의미가 중요한 정보&lt;/b&gt;를 관리할 때 편리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2-7. 변수 타입 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type()을 사용하면 값의 타입을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 20
height = 175.5

print(type(name))
print(type(age))
print(type(height))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;
&amp;lt;class 'int'&amp;gt;
&amp;lt;class 'float'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type()은 객체가 어떤 타입인지 알려주는 내장 함수입니다. &lt;a href=&quot;https://docs.python.org/ko/3/library/functions.html#type&quot;&gt;&lt;span&gt;&lt;span&gt;Python type() 공식 설명&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 결과에 왜 class라는 단어가 나올까요?&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서는 문자열, 정수, 리스트 같은 기본 타입도 모두 클래스로 만들어져 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 클래스를 알아보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 클래스는 왜 필요할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 캐릭터를 관리하는 프로그램을 만든다고 생각해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터가 한 명이라면 다음처럼 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;character1_name = &quot;기사&quot;
character1_hp = 100
character1_level = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 명이 되면 변수가 늘어납니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;character1_name = &quot;기사&quot;
character1_hp = 100
character1_level = 1

character2_name = &quot;마법사&quot;
character2_hp = 70
character2_level = 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터가 100명이라면 어떻게 될까요?&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;character1_name
character1_hp
character1_level

character2_name
character2_hp
character2_level

character3_name
character3_hp
character3_level
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 너무 많아지고 어떤 정보가 누구의 것인지 관리하기 어려워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 &lt;b&gt;클래스&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 클래스는 설계도다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 비슷한 특징을 가진 객체를 만들기 위한 &lt;b&gt;설계도&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 캐릭터에 비유하면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Character 클래스
      │
      ├── 기사 객체
      ├── 마법사 객체
      └── 궁수 객체&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집을 짓는 설계도는 한 장이지만, 같은 설계도로 여러 채의 집을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 클래스는 하나만 정의하고, 그 클래스로 여러 객체를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 공식 문서에서는 새 클래스를 정의하면 새로운 타입이 만들어지고, 그 타입의 인스턴스를 만들 수 있다고 설명합니다. 여기서 인스턴스는 클래스로 만든 실제 객체를 뜻합니다. &lt;a href=&quot;https://docs.python.org/ko/3.10/tutorial/classes.html&quot;&gt;&lt;span&gt;&lt;span&gt;Python 클래스 공식 안내&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 가장 간단한 클래스 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 내용이 없는 간단한 클래스를 만들어 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Character:
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 부분의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Character:
#     클래스 이름&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;class: 클래스를 만들겠다는 Python 문법&lt;/li&gt;
&lt;li&gt;Character: 클래스 이름&lt;/li&gt;
&lt;li&gt;:: 클래스의 내용이 시작된다는 표시&lt;/li&gt;
&lt;li&gt;pass: 아직 작성할 내용이 없다는 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 클래스 이름은 각 단어의 첫 글자를 대문자로 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class GameCharacter:
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 클래스로 객체 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 정의했다면 실제 객체를 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Character:
    pass


warrior = Character()
wizard = Character()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Character는 설계도입니다.&lt;/li&gt;
&lt;li&gt;warrior는 그 설계도로 만든 객체입니다.&lt;/li&gt;
&lt;li&gt;wizard도 같은 설계도로 만든 또 다른 객체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Character 설계도
      │
      ├── warrior
      └── wizard&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 객체는 같은 클래스로 만들었지만 서로 다른 객체입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(warrior)
print(wizard)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하면 두 객체의 메모리 위치가 다르게 표시됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 객체에 정보를 넣어보자&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터마다 이름과 체력이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보는 __init__에서 저장할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보면 복잡해 보이지만 한 부분씩 나누면 어렵지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;__init__은 처음 설정하는 곳&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;__init__은 객체가 만들어질 때 자동으로 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;warrior = Character(&quot;기사&quot;, 100)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 만드는 순간 다음 값이 전달됩니다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;&quot;기사&quot; ──&amp;rarr; name
100     ──&amp;rarr; hp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음 코드가 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;self.name = name
self.hp = hp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 이름과 체력을 해당 객체 안에 저장하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성된 객체는 다음과 같은 정보를 가집니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;warrior
├── name: &quot;기사&quot;
└── hp: 100&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. self는 무엇일까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;self는 &lt;b&gt;현재 작업 중인 객체 자기 자신&lt;/b&gt;을 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 표현하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;self.name은 &amp;ldquo;내 이름&amp;rdquo;, self.hp는 &amp;ldquo;내 체력&amp;rdquo;이라는 뜻입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기사 객체를 만들면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;warrior = Character(&quot;기사&quot;, 100)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;self는 warrior를 가리킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내부적으로 다음과 비슷하게 이해할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;warrior.name = &quot;기사&quot;
warrior.hp = 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마법사 객체를 만들면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;wizard = Character(&quot;마법사&quot;, 70)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 self가 wizard를 가리킵니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;wizard.name = &quot;마법사&quot;
wizard.hp = 70&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, self 덕분에 객체마다 자신의 정보를 따로 보관할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 객체의 정보 꺼내기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점 .을 사용하면 객체가 가진 정보를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp


warrior = Character(&quot;기사&quot;, 100)
wizard = Character(&quot;마법사&quot;, 70)

print(warrior.name)
print(warrior.hp)

print(wizard.name)
print(wizard.hp)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기사
100
마법사
70&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 name과 hp를 &lt;b&gt;속성&lt;/b&gt;이라고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성은 객체가 가지고 있는 정보입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 객체에게 행동을 만들어주자&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터가 자기소개하는 행동을 추가해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def introduce(self):
        print(f&quot;나는 {self.name}입니다.&quot;)
        print(f&quot;현재 체력은 {self.hp}입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 안에 정의된 함수를 &lt;b&gt;메서드&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;warrior = Character(&quot;기사&quot;, 100)
warrior.introduce()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 기사입니다.
현재 체력은 100입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 안의 self.name은 메서드를 실행한 객체의 이름을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;wizard = Character(&quot;마법사&quot;, 70)
wizard.introduce()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 마법사입니다.
현재 체력은 70입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 introduce() 메서드를 실행했지만 객체가 다르므로 결과도 달라집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 공격받는 기능 추가하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 캐릭터의 체력을 줄이는 기능을 만들어 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def introduce(self):
        print(f&quot;나는 {self.name}입니다.&quot;)
        print(f&quot;현재 체력은 {self.hp}입니다.&quot;)

    def take_damage(self, damage):
        self.hp -= damage
        print(f&quot;{self.name}이 {damage}의 피해를 받았습니다.&quot;)
        print(f&quot;남은 체력: {self.hp}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법:&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;warrior = Character(&quot;기사&quot;, 100)

warrior.take_damage(30)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기사가 30의 피해를 받았습니다.
남은 체력: 70&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드에서:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;self.hp -= damage&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 다음 코드와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;self.hp = self.hp - damage&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 객체의 체력에서 피해량을 빼고 다시 저장합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 클래스 코드를 한 줄씩 읽어보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드를 다시 살펴보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def introduce(self):
        print(f&quot;나는 {self.name}입니다.&quot;)

    def take_damage(self, damage):
        self.hp -= damage
        print(f&quot;남은 체력: {self.hp}&quot;)


warrior = Character(&quot;기사&quot;, 100)
warrior.introduce()
warrior.take_damage(30)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 순서대로 해석하면 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Character라는 캐릭터 설계도를 만듭니다.&lt;/li&gt;
&lt;li&gt;캐릭터를 만들 때 이름과 체력을 받습니다.&lt;/li&gt;
&lt;li&gt;받은 이름과 체력을 객체 안에 저장합니다.&lt;/li&gt;
&lt;li&gt;자기소개하는 행동을 정의합니다.&lt;/li&gt;
&lt;li&gt;피해를 받는 행동을 정의합니다.&lt;/li&gt;
&lt;li&gt;이름이 기사이고 체력이 100인 객체를 만듭니다.&lt;/li&gt;
&lt;li&gt;기사가 자기소개합니다.&lt;/li&gt;
&lt;li&gt;기사가 30의 피해를 받습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 클래스의 핵심 용어&lt;/h1&gt;
&lt;div&gt;
&lt;div&gt;용어쉬운 의미코드
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;클래스&lt;/td&gt;
&lt;td&gt;객체를 만드는 설계도&lt;/td&gt;
&lt;td&gt;class Character:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;객체&lt;/td&gt;
&lt;td&gt;설계도로 만든 실제 대상&lt;/td&gt;
&lt;td&gt;warrior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인스턴스&lt;/td&gt;
&lt;td&gt;클래스로 만든 객체를 부르는 말&lt;/td&gt;
&lt;td&gt;warrior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속성&lt;/td&gt;
&lt;td&gt;객체가 가지고 있는 정보&lt;/td&gt;
&lt;td&gt;self.name, self.hp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메서드&lt;/td&gt;
&lt;td&gt;객체가 할 수 있는 행동&lt;/td&gt;
&lt;td&gt;introduce()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__init__&lt;/td&gt;
&lt;td&gt;객체를 처음 만들 때 실행되는 설정&lt;/td&gt;
&lt;td&gt;이름과 체력 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;self&lt;/td&gt;
&lt;td&gt;현재 사용 중인 객체 자기 자신&lt;/td&gt;
&lt;td&gt;self.name&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &lt;b&gt;객체와 인스턴스를 거의 같은 뜻으로 이해해도 괜찮습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 함수와 클래스는 무엇이 다를까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수와 클래스는 서로 경쟁하는 문법이 아닙니다. 사용하는 목적이 다릅니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 작업을 묶을 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def add(a, b):
    return a + b&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 &amp;ldquo;어떤 일을 하는 도구&amp;rdquo;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련된 정보와 행동을 하나로 묶을 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def introduce(self):
        print(self.name)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 &amp;ldquo;정보와 행동을 함께 가진 대상의 설계도&amp;rdquo;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;함수
└── 작업을 묶음

클래스
├── 정보인 속성을 묶음
└── 행동인 메서드를 묶음&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 언제 클래스를 사용하면 좋을까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 같은 구조를 가진 대상을 여러 개 관리해야 할 때 클래스가 유용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 명의 학생&lt;/li&gt;
&lt;li&gt;여러 개의 상품&lt;/li&gt;
&lt;li&gt;여러 대의 자동차&lt;/li&gt;
&lt;li&gt;여러 마리의 강아지&lt;/li&gt;
&lt;li&gt;여러 명의 게임 캐릭터&lt;/li&gt;
&lt;li&gt;여러 개의 은행 계좌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 학생마다 이름과 점수가 있고, 자신의 결과를 출력해야 한다면 클래스로 묶을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def show_result(self):
        print(f&quot;{self.name}: {self.score}점&quot;)


student1 = Student(&quot;민수&quot;, 90)
student2 = Student(&quot;지영&quot;, 85)

student1.show_result()
student2.show_result()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수: 90점
지영: 85점&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;마지막 핵심 정리&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;변수 타입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 가리키는 값의 종류입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;age = 20                     # int
height = 175.5               # float
name = &quot;민수&quot;                # str
is_student = True            # bool
scores = [90, 80, 70]        # list
person = {&quot;name&quot;: &quot;민수&quot;}    # dict&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보와 행동을 함께 묶어 비슷한 객체를 여러 개 만들기 위한 설계도입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class 클래스이름:
    def __init__(self, 값):
        self.속성 = 값

    def 메서드(self):
        실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 다음 세 문장만 기억해도 충분합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 설계도다.&lt;br /&gt;객체는 설계도로 만든 실제 대상이다.&lt;br /&gt;self는 현재 객체 자기 자신이다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/12</guid>
      <comments>https://sojsoj.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 16 Jul 2026 16:42:02 +0900</pubDate>
    </item>
    <item>
      <title>12일차_Vercel배포</title>
      <link>https://sojsoj.tistory.com/11</link>
      <description>&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;Vercel 배포 과정 정리&lt;/h1&gt;
&lt;p style=&quot;color: #6b7280; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Vercel Drop &amp;rarr; CLI &amp;rarr; GitHub 연동 &amp;rarr; Claude 자동화&lt;/p&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;hancom-nine.vercel.app/vercel.html&lt;span&gt;&amp;nbsp;&lt;/span&gt;학습 허브 페이지에 정리된 순서를 바탕으로, 실제로 진행된 배포 흐름(① Vercel Drop 업로드 &amp;rarr; ② CLI 배포 &amp;rarr; ③ GitHub 연동 &amp;rarr; ④ Claude 연동)을 다시 정리한 것입니다. 각 단계는 이전 단계에서 만든 같은 프로젝트&amp;middot;같은 URL을 이어받아 진행됩니다.&lt;/p&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;전체 흐름 한눈에 보기&lt;/h1&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네 단계는 순서대로 이어지며, 뒤 단계로 갈수록 더 적은 수동 작업으로 같은 사이트를 갱신하는 방법을 갖추게 됩니다.&lt;/p&gt;
&lt;div style=&quot;color: #1f2328; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qLeFr/dJMcadiu3dD/vtbQ9rlaJPijHpkX6FiHXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qLeFr/dJMcadiu3dD/vtbQ9rlaJPijHpkX6FiHXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qLeFr/dJMcadiu3dD/vtbQ9rlaJPijHpkX6FiHXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqLeFr%2FdJMcadiu3dD%2FvtbQ9rlaJPijHpkX6FiHXk%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;2352&quot; height=&quot;231&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;단계방법핵심 특징&lt;/p&gt;
&lt;table style=&quot;color: #1f2328; 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 style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;① Vercel Drop&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;브라우저 드래그 앤 드롭&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;Git&amp;middot;CLI&amp;middot;설치 없이 즉시 배포. 일회성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;② CLI 배포&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;vercel / vercel --prod&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;터미널 명령으로 같은 프로젝트에 재배포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;③ GitHub 연동&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;Git 저장소 연결&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;push할 때마다 자동으로 재배포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;④ Claude 연동&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&quot;푸시해줘&quot; 한마디&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;커밋&amp;middot;푸시를 Claude가 대신 실행, 이후는 ③번 자동배포 그대로 작동&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;1단계 &amp;middot; Vercel Drop &amp;mdash; 브라우저에 끌어다 놓기&lt;/h1&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파일&amp;middot;폴더&amp;middot;.zip을 vercel.com/drop 화면에 드래그하면 배포됩니다. Git 저장소도, CLI 설치도 필요 없어 프로토타입이나 정적 사이트를 빠르게 공개할 때 적합합니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;배포 순서 (클릭 4번)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;vercel.com/drop 접속&lt;/li&gt;
&lt;li&gt;파일&amp;middot;폴더&amp;middot;.zip 드래그 (또는 파일/폴더 선택)&lt;/li&gt;
&lt;li&gt;팀 선택 + 프로젝트 이름 입력&lt;/li&gt;
&lt;li&gt;Deploy 클릭 &amp;rarr; Production URL 즉시 발급&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;알아둘 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 사이트는 빌드 없이 그대로 게시되고, 프레임워크 프로젝트는 Vercel이 자동으로 감지해 빌드합니다.&lt;/li&gt;
&lt;li&gt;최상위 폴더에 index.html이 없으면 Vercel이 첫 화면으로 무엇을 보여줄지 물어봅니다. &quot;No root page&quot;를 선택하면 루트(/) 주소는 404가 되고, 각 파일은 자기 경로로만 접속됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;한계 &amp;mdash; 재배포가 안 됩니다&lt;/h2&gt;
&lt;div style=&quot;background-color: #fee2e2; color: #1f2328; text-align: start;&quot;&gt;Vercel Drop에는 &quot;기존 프로젝트에 새 파일 드롭&quot;이라는 기능이 없습니다. 같은 폴더를 또 드롭하면 완전히 새로운 프로젝트와 새로운 URL이 생성됩니다. 같은 URL을 유지하면서 업데이트하려면 2단계(CLI)의&lt;span&gt;&amp;nbsp;&lt;/span&gt;vercel --prod&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는 3단계(GitHub 연동)가 필요합니다.&lt;/div&gt;
&lt;div style=&quot;color: #1f2328; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7JAtX/dJMcahZpTeO/0KnC7Cb73MjrkLjNqS6JP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7JAtX/dJMcahZpTeO/0KnC7Cb73MjrkLjNqS6JP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7JAtX/dJMcahZpTeO/0KnC7Cb73MjrkLjNqS6JP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7JAtX%2FdJMcahZpTeO%2F0KnC7Cb73MjrkLjNqS6JP0%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;2352&quot; height=&quot;207&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;2단계 &amp;middot; CLI 배포 &amp;mdash; 터미널 명령으로 재배포&lt;/h1&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배포란 결국 내 코드를 인터넷 서버에 올려서 누구나 접속할 수 있는 URL을 받는 일입니다. 1단계에서 만든 프로젝트를 같은 URL로 다시 배포하려면 터미널에서 Vercel CLI를 사용합니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;어디서 실행하나요? &amp;mdash; 프로젝트 폴더 안에서&lt;/h2&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vercel&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령은 실행한 현재 디렉토리를 배포 대상으로 인식합니다. package.json이 있는 프로젝트 루트 폴더 안에서 실행해야 하며, 폴더 밖에서 실행하면 엉뚱한 폴더가 올라갑니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;&lt;code&gt;cd my-app        # 프로젝트 루트로 이동 (package.json 있는 곳)
ls                # package.json이 보이면 위치가 맞는 것&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;설치 &amp;middot; 로그인 &amp;middot; 배포&lt;/h2&gt;
&lt;pre class=&quot;coffeescript&quot; style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;&lt;code&gt;npm i -g vercel   # CLI 설치 (최초 1회)
vercel login      # 브라우저 인증 (1회)
vercel            # preview 배포 &amp;mdash; 임시 URL
vercel --prod     # production 배포 &amp;mdash; 실서비스 URL&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;vercel 과 vercel --prod 의 차이&lt;/h2&gt;
&lt;p&gt;명령어결과비고&lt;/p&gt;
&lt;table style=&quot;color: #1f2328; 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 style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;vercel&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;Preview 배포 (임시 URL)&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;실행할 때마다 새 임시 URL이 생기고 실서비스에는 영향 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;vercel --prod&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;Production 배포 (실서비스 URL)&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;1단계 Drop에서 만든 것과 같은 도메인이 갱신됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div style=&quot;background-color: #fef3c7; color: #1f2328; text-align: start;&quot;&gt;오타 주의:&lt;span&gt;&amp;nbsp;&lt;/span&gt;--prod를&lt;span&gt;&amp;nbsp;&lt;/span&gt;--pord&lt;span&gt;&amp;nbsp;&lt;/span&gt;등으로 잘못 입력하지 않도록 확인합니다.&lt;/div&gt;
&lt;div style=&quot;color: #1f2328; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVErxS/dJMcahZpTeP/fXYyck78rQDznVPaFizQyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVErxS/dJMcahZpTeP/fXYyck78rQDznVPaFizQyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVErxS/dJMcahZpTeP/fXYyck78rQDznVPaFizQyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVErxS%2FdJMcahZpTeP%2FfXYyck78rQDznVPaFizQyk%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;2352&quot; height=&quot;486&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;3단계 &amp;middot; GitHub 연동 &amp;mdash; push하면 자동 배포&lt;/h1&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GitHub 저장소를 Vercel 프로젝트에 연결하면, 그 뒤로는 git push할 때마다 Vercel이 자동으로 재배포합니다. 코드만 올리면 배포는 자동으로 이루어지는, 실무에서 가장 널리 쓰이는 방식입니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;GitHub에 코드 올리기&lt;/h2&gt;
&lt;pre class=&quot;properties&quot; style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;&lt;code&gt;git init
git add .
git commit -m &quot;first deploy&quot;
git branch -M main
git remote add origin https://github.com/내아이디/my-app.git
git push -u origin main&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Vercel 웹에서 저장소 연동하기 (권장)&lt;/h2&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2단계에서 CLI로 만든 프로젝트에 Git을 연결하는 방법입니다. 클릭만으로 끝나며 CLI 인증이 꼬일 일이 없습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Vercel 웹사이트 로그인 &amp;rarr; 해당 프로젝트 클릭&lt;/li&gt;
&lt;li&gt;프로젝트 대시보드 진입 &amp;rarr; 좌측 사이드바 맨 아래 Settings 클릭&lt;/li&gt;
&lt;li&gt;Settings 탭 목록에서 Git 선택&lt;/li&gt;
&lt;li&gt;Connect Git Repository 클릭 &amp;rarr; GitHub 저장소 선택 후 연동&lt;/li&gt;
&lt;li&gt;연동 후 git push &amp;rarr; 자동 재배포가 켜짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.vercel/ 폴더는 프로젝트 연결 정보이므로 git에 커밋하지 않습니다.&lt;/li&gt;
&lt;li&gt;.gitignore에 node_modules, dist, .vercel이 포함되어 있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;모노레포 구조라면 Settings &amp;rarr; Root Directory에서 배포할 하위 폴더를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;color: #1f2328; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYxOD5/dJMcafm5LFH/tulwUNnwvOF0BUYEXBdWh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYxOD5/dJMcafm5LFH/tulwUNnwvOF0BUYEXBdWh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYxOD5/dJMcafm5LFH/tulwUNnwvOF0BUYEXBdWh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYxOD5%2FdJMcafm5LFH%2FtulwUNnwvOF0BUYEXBdWh0%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;2352&quot; height=&quot;177&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;177&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;4단계 &amp;middot; Claude 연동 &amp;mdash; &quot;푸시해줘&quot; 한마디로 배포&lt;/h1&gt;
&lt;p style=&quot;color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3단계에서 GitHub와 Vercel이 이미 연결되어 있으므로, 이제부터는 Claude에게 코드 작업을 시키고 &quot;깃허브에 푸시해줘&quot;라고만 하면 Vercel에 자동으로 반영됩니다. 터미널이나 CLI를 직접 만질 필요가 없습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;흐름&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Claude에게 코드 수정 요청&lt;/li&gt;
&lt;li&gt;&quot;깃허브 푸시해줘&quot; 한마디&lt;/li&gt;
&lt;li&gt;Claude가 git add &amp;middot; commit &amp;middot; push 실행&lt;/li&gt;
&lt;li&gt;Vercel이 push를 감지해 자동 재배포&lt;/li&gt;
&lt;li&gt;잠시 뒤 실서비스 URL에 반영&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 style=&quot;color: #111827; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예시 대화&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f6f8fa; color: #1f2328; text-align: start;&quot;&gt;&lt;code&gt;나: 버튼 색 파란색으로 바꾸고 깃허브에 푸시해줘

Claude: 색상 변경 완료. 커밋&amp;middot;푸시했습니다.
&amp;rarr; Vercel이 배포 시작 (1~2분 후 URL 반영)&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #fee2e2; color: #1f2328; text-align: start;&quot;&gt;주의: 커밋 author 이메일이 Vercel 계정과 연결된 이메일과 일치해야 정상적으로 배포됩니다. 배포가 안 되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;git config user.email&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 확인합니다.&lt;/div&gt;
&lt;div style=&quot;color: #1f2328; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z77FP/dJMcahZpTeQ/hPyXbbBkBEC3NTaKh51Xt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z77FP/dJMcahZpTeQ/hPyXbbBkBEC3NTaKh51Xt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z77FP/dJMcahZpTeQ/hPyXbbBkBEC3NTaKh51Xt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ77FP%2FdJMcahZpTeQ%2FhPyXbbBkBEC3NTaKh51Xt1%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;2352&quot; height=&quot;189&quot; data-origin-width=&quot;2352&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;자주 쓰는 설정 위치 요약&lt;/h1&gt;
&lt;p&gt;하고 싶은 일메뉴 경로&lt;/p&gt;
&lt;table style=&quot;color: #1f2328; 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 style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;GitHub 저장소 연결/변경&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;프로젝트 &amp;rarr; Settings &amp;rarr; Git &amp;rarr; Connect Git Repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;배포 브랜치(Production Branch) 변경&lt;/td&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;프로젝트 &amp;rarr; Settings &amp;rarr; Environments &amp;rarr; Production &amp;rarr; Branch Tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;코드 변경 없이 재빌드만&lt;/td&gt;
&lt;td style=&quot;background-color: #f9fafb; text-align: left;&quot;&gt;프로젝트 &amp;rarr; Deployments &amp;rarr; 최신 배포 ⋯ &amp;rarr; Redeploy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div style=&quot;background-color: #fef3c7; color: #1f2328; text-align: start;&quot;&gt;연결한 작업 브랜치와 Production 브랜치(기본값 main)가 다르면 push해도 자동 배포가 되지 않습니다. Branch Tracking 설정을 원하는 브랜치로 바꾸거나, 작업 브랜치를 main에 머지해서 push하면 해결됩니다.&lt;/div&gt;
&lt;h1 style=&quot;color: #1f2328; text-align: start;&quot;&gt;한 장 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Vercel Drop = 일회성 배포. 다시 드롭하면 새 프로젝트가 생성됨&lt;/li&gt;
&lt;li&gt;같은 URL로 계속 업데이트하려면 CLI의 vercel --prod 또는 GitHub 연결이 필요&lt;/li&gt;
&lt;li&gt;GitHub 연결은 Settings &amp;rarr; Git &amp;rarr; Connect Git Repository&lt;/li&gt;
&lt;li&gt;배포 브랜치 변경은 Settings &amp;rarr; Environments &amp;rarr; Production &amp;rarr; Branch Tracking&lt;/li&gt;
&lt;li&gt;자동 배포 조건은 Production 브랜치에 push하는 것 (브랜치가 다르면 배포 안 됨)&lt;/li&gt;
&lt;li&gt;GitHub 연동이 끝나면 Claude에게 &quot;푸시해줘&quot;라고만 해도 배포가 자동으로 이어짐&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/11</guid>
      <comments>https://sojsoj.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 15 Jul 2026 16:47:17 +0900</pubDate>
    </item>
    <item>
      <title>12일차_Node&amp;middot;Express /POST 보내기/</title>
      <link>https://sojsoj.tistory.com/10</link>
      <description>&lt;h1&gt;초보자를 위한 Express POST 완전 안내서&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. POST란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프로그램에서는 보통 다음과 같은 왕복이 일어납니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;사용자 입력
   &amp;darr;
프론트엔드가 서버에 POST 요청
   &amp;darr;
서버가 req.body에서 데이터 확인
   &amp;darr;
서버가 처리 결과를 JSON으로 응답
   &amp;darr;
프론트엔드가 응답을 받아 화면에 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI 채팅에서는 사용자가 작성한 질문을 서버로 보내야 합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;질문 작성 &amp;rarr; POST 전송 &amp;rarr; AI 서버 처리 &amp;rarr; 답변 수신&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입, 로그인, 글쓰기, 주문, 댓글 작성, 파일 등록 등도 대체로 POST 또는 비슷한 쓰기용 HTTP 메서드를 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. GET과 POST의 기본 차이&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;구분GETPOST
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;주요 목적&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;데이터 조회&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;데이터 제출&amp;middot;생성&amp;middot;처리 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;데이터 위치&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;주로 URL 쿼리 문자열&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;주로 요청 body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;주소창 실행&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;가능&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;일반적인 주소창 입력으로는 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;대표 사례&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;상품 목록, 검색, 게시글 조회&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;회원가입, 글쓰기, AI 질문&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;새로고침&amp;middot;공유&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;비교적 적합&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;같은 요청이 다시 실행될 수 있어 주의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;긴 데이터 전송&lt;/td&gt;
&lt;td style=&quot;width: 37.093%;&quot;&gt;부적합&lt;/td&gt;
&lt;td style=&quot;width: 45.1163%;&quot;&gt;비교적 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 요청의 예:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /api/products?category=book&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST 요청의 예:&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;POST /api/chat
Content-Type: application/json

{
  &quot;message&quot;: &quot;POST가 무엇인가요?&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 POST라고 해서 데이터가 자동으로 안전해지는 것은 아니라는 사실입니다. POST 데이터는 주소창에는 나타나지 않지만, 암호화하려면 반드시 HTTPS를 사용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 실습 준비&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서는 다음 프로그램 두 개를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;index.js  &amp;rarr; 메시지를 받는 서버
send.js   &amp;rarr; 메시지를 보내는 클라이언트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 폴더 구조:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;08_node_express/
└─ 06_post/
   ├─ index.js
   ├─ send.js
   ├─ package.json
   └─ node_modules/&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Node.js 버전 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 다음 명령을 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node -v
npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch()를 별도 패키지 없이 사용하려면 Node.js 18 이상이 편리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 만들기&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;mkdir 08_node_express\06_post
cd 08_node_express\06_post
npm init -y
npm install express&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 명령의 의미:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mkdir: 폴더 만들기&lt;/li&gt;
&lt;li&gt;cd: 해당 폴더로 이동하기&lt;/li&gt;
&lt;li&gt;npm init -y: Node.js 프로젝트 설정 파일 만들기&lt;/li&gt;
&lt;li&gt;npm install express: Express 설치하기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 서버 만들기: index.js&lt;/h1&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const express = require('express')
const app = express()

// JSON 형식으로 전송된 요청 본문을 자바스크립트 객체로 변환합니다.
app.use(express.json())

app.post('/api/chat', (req, res) =&amp;gt; {
  const { message } = req.body

  console.log('받은 메시지:', message)

  res.json({
    ok: true,
    받은문장: message
  })
})

app.listen(3000, () =&amp;gt; {
  console.log('서버 실행: http://localhost:3000')
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 줄씩 이해하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Express 불러오기&lt;/h3&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;const express = require('express')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치한 Express 라이브러리를 현재 파일에서 사용할 수 있게 가져옵니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Express 앱 만들기&lt;/h3&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;const app = express()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app은 서버를 설정하고 요청을 처리하는 중심 객체입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JSON 해석 기능 켜기&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;app.use(express.json())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 보내는 JSON 문자열을 자바스크립트 객체로 변환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 다음 JSON을 보냈다고 가정해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;안녕하세요&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;express.json()이 있으면 서버에서 다음과 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;req.body.message&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;안녕하세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;express.json()이 없으면 req.body가 undefined가 되어 데이터를 읽지 못할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;POST 요청 처리하기&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;app.post('/api/chat', (req, res) =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;POST 방식으로 /api/chat 주소에 요청이 오면 이 함수를 실행한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 /api/chat이라도 요청 방식이 다르면 다른 요청입니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;GET  /api/chat
POST /api/chat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 서버에는 POST 처리만 있으므로 브라우저 주소창에서 http://localhost:3000/api/chat을 열면 원하는 결과가 나오지 않습니다. 주소창은 기본적으로 GET 요청을 보내기 때문입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조 분해 할당으로 message 꺼내기&lt;/h3&gt;
&lt;pre class=&quot;d&quot;&gt;&lt;code&gt;const { message } = req.body&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드의 짧은 표현입니다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;const message = req.body.message&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버 터미널에 출력하기&lt;/h3&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;console.log('받은 메시지:', message)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용은 사용자의 브라우저가 아니라 node index.js를 실행한 서버 터미널에 표시됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JSON으로 답장하기&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;res.json({
  ok: true,
  받은문장: message
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 클라이언트에게 다음과 같은 응답을 보냅니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;ok&quot;: true,
  &quot;받은문장&quot;: &quot;안녕하세요&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;res.json()은 객체를 JSON 문자열로 변환하고 JSON 응답이라는 정보도 함께 설정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3000번 포트에서 기다리기&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;app.listen(3000, () =&amp;gt; {
  console.log('서버 실행: http://localhost:3000')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 3000번 포트를 사용해 요청을 기다립니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 입력받아 POST 보내기: send.js&lt;/h1&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const readline = require('readline')

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})

rl.question('메시지: ', async (message) =&amp;gt; {
  try {
    const response = await fetch('http://localhost:3000/api/chat', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ message })
    })

    if (!response.ok) {
      throw new Error(`서버 오류: ${response.status}`)
    }

    const result = await response.json()
    console.log('서버 답장:', result)
  } catch (error) {
    console.error('요청 실패:', error.message)
    console.log('서버를 먼저 실행했는지 확인하세요.')
  } finally {
    rl.close()
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 .then() 방식도 올바르지만, 초보자가 실행 순서를 읽기 쉬운 async/await 형태로 작성한 예제입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력창 만들기&lt;/h2&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;const readline = require('readline')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 터미널에서 사용자의 키보드 입력을 받기 위한 기본 모듈입니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;process.stdin: 키보드로 입력받기&lt;/li&gt;
&lt;li&gt;process.stdout: 터미널에 글자 출력하기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메시지 입력받기&lt;/h2&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;rl.question('메시지: ', async (message) =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에 다음과 같이 표시됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;메시지:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 문장을 작성하고 Enter를 누르면 그 내용이 message 변수에 저장됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fetch로 POST 요청 보내기&lt;/h2&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;const response = await fetch('http://localhost:3000/api/chat', {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch()는 다른 서버 주소로 HTTP 요청을 보내는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;await는 서버 응답이 도착할 때까지 해당 함수 안에서 기다린다는 뜻입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;POST 방식 지정하기&lt;/h2&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;method: 'POST'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청이 데이터 제출을 위한 POST 요청이라고 지정합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JSON이라는 사실 알리기&lt;/h2&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;headers: {
  'Content-Type': 'application/json'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에게 다음과 같이 알려주는 꼬리표입니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;요청 body에 JSON을 넣어서 보냅니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 express.json()은 이 정보를 보고 JSON 본문을 해석합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;객체를 JSON 문자열로 바꾸기&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;body: JSON.stringify({ message })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{ message }는 다음의 축약 표현입니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{
  message: message
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 반갑습니다라고 입력했다면:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{ message: '반갑습니다' }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON.stringify()를 거치면 전송할 수 있는 JSON 문자열이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{&quot;message&quot;:&quot;반갑습니다&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서버 응답 확인하기&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;if (!response.ok) {
  throw new Error(`서버 오류: ${response.status}`)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 400, 404, 500 같은 오류 상태를 돌려준 경우 오류로 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch()는 서버가 404나 500을 보내도 자동으로 catch로 넘어가지 않기 때문에 response.ok 확인이 중요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;응답 JSON을 객체로 변환하기&lt;/h2&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;const result = await response.json()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 보낸 JSON 문자열을 자바스크립트 객체로 변환합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;console.log('서버 답장:', result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;서버 답장: { ok: true, '받은문장': '안녕하세요' }&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 실행 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널을 두 개 엽니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;첫 번째 터미널: 서버 실행&lt;/h2&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node index.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;서버 실행: http://localhost:3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 터미널은 종료하지 말고 그대로 둡니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;두 번째 터미널: 메시지 보내기&lt;/h2&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node send.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력:&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;메시지: POST 연습 중입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보내는 쪽 출력:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;서버 답장: { ok: true, '받은문장': 'POST 연습 중입니다' }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 쪽 출력:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;받은 메시지: POST 연습 중입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 다음 왕복이 완료된 것입니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;send.js
  └─ body로 메시지 전송
       &amp;darr;
index.js
  └─ req.body에서 메시지 확인
       &amp;darr;
index.js
  └─ res.json()으로 응답
       &amp;darr;
send.js
  └─ response.json()으로 응답 확인&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 데이터가 실제로 어떻게 변하는지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 다음 문장을 입력했다고 가정해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;오늘 날씨가 어때?&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 입력 직후&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;message === '오늘 날씨가 어때?'&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 자바스크립트 객체 만들기&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{ message: '오늘 날씨가 어때?' }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ JSON 문자열로 변환하기&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;JSON.stringify({ message })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{&quot;message&quot;:&quot;오늘 날씨가 어때?&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;④ 서버가 JSON 해석하기&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;app.use(express.json())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 req.body:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{
  message: '오늘 날씨가 어때?'
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⑤ 서버가 message 꺼내기&lt;/h2&gt;
&lt;pre class=&quot;d&quot;&gt;&lt;code&gt;const { message } = req.body&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⑥ 서버가 결과 보내기&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;res.json({
  ok: true,
  받은문장: message
})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⑦ 클라이언트가 응답 해석하기&lt;/h2&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;const result = await response.json()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 객체:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{
  ok: true,
  받은문장: '오늘 날씨가 어때?'
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 안전한 서버 코드로 개선하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 코드에서는 body가 없거나 message가 잘못 들어오면 문제가 생길 수 있습니다. 실제 서버에 가깝게 검증 기능을 넣어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;const express = require('express')
const app = express()

app.use(express.json())

app.post('/api/chat', (req, res) =&amp;gt; {
  const { message } = req.body

  if (typeof message !== 'string' || message.trim() === '') {
    return res.status(400).json({
      ok: false,
      error: 'message를 문자열로 입력해 주세요.'
    })
  }

  const cleanMessage = message.trim()

  console.log('받은 메시지:', cleanMessage)

  res.status(200).json({
    ok: true,
    받은문장: cleanMessage,
    글자수: cleanMessage.length
  })
})

app.listen(3000, '0.0.0.0', () =&amp;gt; {
  console.log('서버 실행: http://localhost:3000')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증한 내용:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;message가 실제 문자열인지 확인&lt;/li&gt;
&lt;li&gt;빈 문장인지 확인&lt;/li&gt;
&lt;li&gt;앞뒤 공백 제거&lt;/li&gt;
&lt;li&gt;문제가 있으면 400 Bad Request 반환&lt;/li&gt;
&lt;li&gt;성공하면 200 OK 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return이 중요한 이유:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;return res.status(400).json(...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 응답을 보낸 뒤 함수 실행까지 즉시 끝냅니다. return이 없으면 아래의 성공 응답도 보내려다가 &amp;ldquo;응답을 두 번 보냈다&amp;rdquo;는 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. HTML 페이지에서 POST 보내기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널 프로그램이 아니라 실제 웹페이지에서도 원리는 같습니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
  &amp;lt;title&amp;gt;POST 채팅 연습&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;메시지 보내기&amp;lt;/h1&amp;gt;

  &amp;lt;input id=&quot;message&quot; placeholder=&quot;메시지를 입력하세요&quot;&amp;gt;
  &amp;lt;button id=&quot;sendButton&quot;&amp;gt;보내기&amp;lt;/button&amp;gt;

  &amp;lt;pre id=&quot;result&quot;&amp;gt;&amp;lt;/pre&amp;gt;

  &amp;lt;script&amp;gt;
    const messageInput = document.querySelector('#message')
    const sendButton = document.querySelector('#sendButton')
    const resultBox = document.querySelector('#result')

    sendButton.addEventListener('click', async () =&amp;gt; {
      const message = messageInput.value

      try {
        const response = await fetch('/api/chat', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ message })
        })

        const result = await response.json()

        if (!response.ok) {
          throw new Error(result.error || '요청에 실패했습니다.')
        }

        resultBox.textContent = JSON.stringify(result, null, 2)
      } catch (error) {
        resultBox.textContent = error.message
      }
    })
  &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 HTML 파일을 제공하려면 public/index.html로 저장하고 다음 코드를 추가할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;app.use(express.static('public'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 브라우저에서 아래 주소를 엽니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 버튼을 누르면 웹페이지가 POST 요청을 보냅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. Claude&amp;middot;ChatGPT 같은 AI 서비스와의 관계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 채팅 서비스도 개념적으로 비슷한 흐름을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자가 프롬프트 작성
&amp;rarr; 웹페이지가 서버에 요청
&amp;rarr; 서버가 AI 모델을 호출
&amp;rarr; 생성된 답변을 웹페이지에 응답
&amp;rarr; 웹페이지가 답변 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 예제와 실제 AI 서비스의 차이도 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 정보가 추가될 수 있음&lt;/li&gt;
&lt;li&gt;대화 기록이 함께 전송될 수 있음&lt;/li&gt;
&lt;li&gt;응답이 한꺼번에 오지 않고 스트리밍될 수 있음&lt;/li&gt;
&lt;li&gt;오류 처리와 사용량 제한이 있음&lt;/li&gt;
&lt;li&gt;보안, 저장, 필터링 같은 서버 처리가 추가됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 전체 원리는 같지만, 실제 서비스가 반드시 단순 POST 한 번만 사용한다고 단정할 수는 없습니다. 스트리밍을 위해 SSE, WebSocket 또는 스트리밍 HTTP 응답을 사용할 수도 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. curl로 POST 요청 보내기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;send.js 없이도 curl로 서버를 시험할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PowerShell에서는 따옴표 충돌을 줄이기 위해 다음 형태가 편리합니다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;curl.exe -X POST &quot;http://localhost:3000/api/chat&quot; `
  -H &quot;Content-Type: application/json&quot; `
  -d '{\&quot;message\&quot;:\&quot;curl에서 보낸 인사\&quot;}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄 버전:&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;curl.exe -X POST &quot;http://localhost:3000/api/chat&quot; -H &quot;Content-Type: application/json&quot; -d '{\&quot;message\&quot;:\&quot;안녕하세요\&quot;}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에 따라 PowerShell의 따옴표 처리 방식이 다르게 느껴질 수 있습니다. 이때는 다음 명령도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;$body = @{ message = &quot;PowerShell에서 보낸 인사&quot; } | ConvertTo-Json

Invoke-RestMethod `
  -Uri &quot;http://localhost:3000/api/chat&quot; `
  -Method Post `
  -ContentType &quot;application/json&quot; `
  -Body $body&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;nbsp;우리반 배치도 만들기&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;* GET과 POST의 차이&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET은 서버에서 데이터를 받아올 때 사용한다.&lt;/li&gt;
&lt;li&gt;POST는 서버에 데이터를 보내 새로운 데이터를 만들 때 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학생 명단 조회: GET&lt;/li&gt;
&lt;li&gt;회원가입: POST&lt;/li&gt;
&lt;li&gt;글 작성: POST&lt;/li&gt;
&lt;li&gt;AI에게 질문 보내기: POST&lt;/li&gt;
&lt;li&gt;새로운 학생 추가: POST&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &amp;ldquo;GET은 받기, POST는 보내기&amp;rdquo;라고만 외우면 부족하다. GET도 요청을 보내고 응답을 받으며, POST도 데이터를 보낸 뒤 서버의 응답을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 둘 다 요청과 응답의 왕복이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;브라우저 &amp;rarr; 서버에 요청
브라우저 &amp;larr; 서버의 응답&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. fetch 요청을 구성하는 네 가지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 사용한 fetch 코드는 다음과 같은 형태였다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;fetch('http://192.168.10.28:5000/hancom/이름/users', {
  method: 'POST',

  headers: {
    'Content-Type': 'application/json',
    Authorization: 'HANCOM'
  },

  body: JSON.stringify({
    name: '홍길동'
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;fetch 요청&lt;/b&gt;은 크게 네 부분으로 생각하면 쉽다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. URL&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 받을 서버의 주소다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;'http://192.168.10.28:5000/hancom/이름/users'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소는 다시 다음과 같이 나눌 수 있다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;http://192.168.10.28:5000  &amp;rarr; 서버 주소와 포트
/hancom/이름/users       &amp;rarr; API 경로&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. method&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 어떤 행동을 요청할지 정한다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;method: 'POST'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 요청은 기본값이므로 생략할 수 있지만, 초보 단계에서는 의도를 분명하게 표시해도 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. headers&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 어떤 요청을 보내는지 서버에 알려주는 정보다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;headers: {
  'Content-Type': 'application/json',
  Authorization: 'HANCOM'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Content-Type은 body에 JSON을 보낸다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authorization은 서버가 요청을 허용할지 판단할 때 사용하는 인증 정보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 나는 토큰을 주소창이나 HTML 화면에 입력해야 하는지 헷갈렸다. 하지만 토큰은 fetch의 headers 안에 넣는 것이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. body&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 전달할 실제 데이터다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;body: JSON.stringify({
  name: '홍길동'
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 객체는 그대로 전송하지 않고 JSON.stringify()를 사용해 JSON 문자열로 변환한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 서버에서는 데이터를 어떻게 받는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Express 서버에서는 다음 코드가 중요하다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;app.use(express.json())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 보낸 JSON 문자열을 해석해서 req.body로 사용할 수 있게 해준다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;app.post('/api/chat', (req, res) =&amp;gt; {
  const { message } = req.body

  console.log('받은 메시지:', message)

  res.json({
    ok: true,
    받은문장: message
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 왕복은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;사용자가 문장을 입력
        &amp;darr;
fetch의 body로 전송
        &amp;darr;
Express가 express.json()으로 해석
        &amp;darr;
req.body에서 값 꺼내기
        &amp;darr;
서버가 res.json()으로 응답
        &amp;darr;
브라우저가 response.json()으로 받기
        &amp;darr;
화면에 결과 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한 쌍으로 기억할 것이 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;보내는 쪽: JSON.stringify()
받는 서버: express.json()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;express.json()이 없으면 req.body가 undefined로 나올 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 응답을 받는 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 응답도 JSON 문자열 상태로 도착하기 때문에 JavaScript 객체로 바꿔야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;fetch(url, options)
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; {
    console.log(data)
  })
  .catch(error =&amp;gt; {
    console.error(error)
  })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계의 의미는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;.then(response =&amp;gt; response.json())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 JSON 응답을 JavaScript에서 사용할 수 있는 값으로 해석한다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;.then(data =&amp;gt; {
  console.log(data)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해석된 데이터를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;.catch(error =&amp;gt; {
  console.error(error)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 과정에서 발생한 오류를 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 오늘 수행한 CRUD 과제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 핵심 과제는 오염된 수강생 명단을 정상 명단으로 정리하고 HTML 배치도로 보여주는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRUD는 다음 네 가지 작업을 의미한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;작업HTTP 메서드의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;새로운 학생 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;학생 명단 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;기존 학생 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;학생 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GET: 명단 조회&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;fetch(`${BASE_URL}/hancom/${CLASS_NAME}/users`, {
  headers: {
    Authorization: 'HANCOM'
  }
})
  .then(response =&amp;gt; response.json())
  .then(students =&amp;gt; {
    console.log(students)
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;POST: 학생 추가&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;fetch(`${BASE_URL}/hancom/${CLASS_NAME}/users`, {
  method: 'POST',
  headers: {
    Authorization: 'HANCOM',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: '추가할 이름'
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PUT: 이름 수정&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;fetch(`${BASE_URL}/hancom/${CLASS_NAME}/users/${id}`, {
  method: 'PUT',
  headers: {
    Authorization: 'HANCOM',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: '새로운 이름'
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DELETE: 학생 삭제&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;fetch(`${BASE_URL}/hancom/${CLASS_NAME}/users/${id}`, {
  method: 'DELETE',
  headers: {
    Authorization: 'HANCOM'
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요청에는 다음 인증 헤더가 필요했다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Authorization: 'HANCOM'&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 이름과 ID는 다르다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 발생한 문제 중 하나는 학생 이름을 수정했더니 다른 자리가 바뀌거나 중복이 생기는 현상이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 사실은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;이름 = 변경할 수 있는 정보
ID = 한 사람을 정확하게 찾는 고유 주소&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 두 데이터가 있으면 문제가 된다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{ id: 1, name: '박진' }
{ id: 1, name: '양하은' }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ID가 같기 때문에 서버는 어떤 학생을 수정해야 하는지 정확히 구분하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 데이터는 다음과 같아야 한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{ id: 1, name: '박진' }
{ id: 2, name: '양하은' }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PUT과 DELETE는 이름이 아니라 ID를 기준으로 대상을 찾는다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;PUT /users/14
DELETE /users/14&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이름 중복뿐 아니라 ID 중복도 반드시 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 서버 데이터와 좌석 데이터를 섞으면서 생긴 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과제를 진행하면서 자리 이동, 빈자리, 자리 없는 사람, 자동 정리 등의 기능을 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디어 자체는 좋았지만, 서버 명단과 브라우저 좌석 정보가 동시에 학생 상태를 관리하면서 충돌이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 두 곳에서 정보를 관리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;강사 서버: 14번 ID 학생은 양하은
브라우저 좌석 정보: 14번 좌석은 박진&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 데이터의 주인이 두 곳이 되면 다음 문제가 생길 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름을 수정했는데 빈자리로 이동함&lt;/li&gt;
&lt;li&gt;한 사람이 두 자리에 나타남&lt;/li&gt;
&lt;li&gt;화면과 서버의 명단 수가 다름&lt;/li&gt;
&lt;li&gt;삭제 버튼을 두 번 눌러야 반영됨&lt;/li&gt;
&lt;li&gt;누락 인원 수와 화면 명단 수가 다름&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 줄이려면 서버를 명단의 기준으로 삼는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;서버 데이터 변경
      &amp;darr;
GET으로 최신 명단 다시 조회
      &amp;darr;
받은 데이터로 화면 전체를 다시 그림&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에서 값을 억지로 예상해서 바꾸는 것보다 서버의 최신 데이터를 다시 받아오는 방식이 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. AI 질문 기능도 같은 POST 구조였다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Groq를 사용한 AI 질문 기능도 기본 구조는 같았다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;브라우저 입력
     &amp;darr;
내 Express 서버에 POST
     &amp;darr;
Express 서버가 Groq API에 요청
     &amp;darr;
Groq의 답변
     &amp;darr;
Express 서버가 브라우저에 응답
     &amp;darr;
화면에 AI 답변 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 Groq API에 직접 요청하지 않고 내 서버를 거치는 중요한 이유는 API 키를 보호하기 위해서다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;client/index.html
    &amp;darr; 질문만 전송
server/index.js
    &amp;darr; .env의 API 키 사용
Groq API&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 키는 클라이언트 HTML에 넣으면 안 된다. 브라우저에서 누구나 확인할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 서버가 켜진 것처럼 보였지만 실제로 종료된 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에 다음과 같이 출력되었다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;http://localhost:3000
PS C:\프로젝트\server&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소가 출력됐기 때문에 서버가 실행됐다고 생각하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 PowerShell 입력 프롬프트가 바로 돌아왔다면 Node 프로세스가 종료됐을 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 서버가 계속 실행 중이라면 일반적으로 해당 터미널은 서버 프로세스가 차지하고 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 이미 3000번 포트를 다른 프로그램이 사용하고 있어 충돌이 생겼고, 서버와 클라이언트를 3001번 포트로 맞췄다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;app.listen(3001)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fetch('http://localhost:3001/api/chat')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 배운 중요한 기준은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;fix&quot;&gt;&lt;code&gt;서버가 듣는 포트 = 클라이언트가 요청하는 포트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 하나만 바꾸면 연결되지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 오류 메시지를 해석하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 화면이 작동하지 않는다고 무작정 코드를 여러 군데 고치지 말아야겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;401 Unauthorized&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 정보가 없거나 잘못된 경우다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 부분:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;headers: {
  Authorization: 'HANCOM'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소창으로 API에 직접 접속하면 Authorization 헤더를 넣을 수 없기 때문에 401이 나올 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;req.body가 undefined&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 다음 코드가 있는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;app.use(express.json())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트도 다음 두 설정을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;headers: {
  'Content-Type': 'application/json'
}

body: JSON.stringify(data)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서버 안 켜짐 또는 fetch failed&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 순서로 확인한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버 터미널이 계속 실행 중인가?&lt;/li&gt;
&lt;li&gt;서버 주소가 맞는가?&lt;/li&gt;
&lt;li&gt;포트가 같은가?&lt;/li&gt;
&lt;li&gt;서버 라우트가 맞는가?&lt;/li&gt;
&lt;li&gt;브라우저 Console에 실제 오류가 무엇인가?&lt;/li&gt;
&lt;li&gt;서버 터미널에는 어떤 오류가 있는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이름 수정 후 중복이나 빈자리 발생&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 항목을 확인한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ID가 중복됐는가?&lt;/li&gt;
&lt;li&gt;PUT URL에 올바른 ID를 넣었는가?&lt;/li&gt;
&lt;li&gt;서버 데이터와 별도의 좌석 데이터가 충돌하는가?&lt;/li&gt;
&lt;li&gt;수정 후 GET으로 최신 데이터를 다시 받았는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/10</guid>
      <comments>https://sojsoj.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 14 Jul 2026 16:47:09 +0900</pubDate>
    </item>
    <item>
      <title>10일차_React (components)</title>
      <link>https://sojsoj.tistory.com/9</link>
      <description>&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;1. 리액트 컴포넌트(Component)의 생성과 사용&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트는 재사용할 수 있는 독립적인 'UI 조각'입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;한 번 정의해 두면 원하는 곳에서 여러 번 반복해서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;① 컴포넌트 만들기 (예시 - Hello.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;components 폴더에 Hello.jsx 파일을 생성하고 화면에 렌더링할 함수를 작성합니다.&lt;/li&gt;
&lt;li&gt;파일 하단에 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;export default Hello&lt;/b&gt;&lt;/span&gt;를 작성합니다.&lt;span style=&quot;color: #8a3db6;&quot;&gt; (이 컴포넌트를 외부 파일에서 사용할 수 있도록 내보내는 역할)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;② 메인 화면에서 조립하기 (App.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;불러오기:&lt;/b&gt; 파일 상단에&lt;span style=&quot;color: #8a3db6;&quot;&gt; import Hello from './components/Hello.jsx'&lt;/span&gt;를 작성해 &lt;span style=&quot;color: #8a3db6;&quot;&gt;컴포넌트를 가져옵니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;사용하기:&lt;/b&gt; 화면에 출력할 위치에 &amp;lt;Hello /&amp;gt; 태그 형태로 작성합니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;lt;Hello /&amp;gt;를 여러 번 작성하면 화면에도 동일한 UI가 여러 번 렌더링됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. 컴포넌트 작성 시 3가지 필수 규칙&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;오류 없이 컴포넌트를 화면에 렌더링하기 위해 반드시 지켜야 하는 규칙입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;① 컴포넌트 이름의 첫 글자는 반드시 대문자&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(O) &amp;lt;Hello /&amp;gt; / (X) &amp;lt;hello /&amp;gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,1,1,0&quot;&gt;이유:&lt;/b&gt; 리액트는 첫 글자가 소문자인 태그를 기존 HTML 태그(&amp;lt;div&amp;gt;, &amp;lt;h1&amp;gt; 등)로 인식하기 때문에, 개발자가 만든 컴포넌트임을 명시하기 위해 대문자를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;② 반환(return) 시 최상위 부모 태그는 오직 1개&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리액트는 여러 개의 요소를 병렬로 반환할 수 없으므로, 전체를 감싸는 하나의 래퍼(Wrapper) 태그가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,1,1,0&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;빈 태그 &amp;lt;&amp;gt;&amp;lt;/&amp;gt; (Fragment) 활용:&lt;/span&gt;&lt;/b&gt; 화면 코드(DOM)에 불필요한 &amp;lt;div&amp;gt;를 남기지 않고 구조만 묶고 싶을 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;③ 파일 하단에 &lt;span style=&quot;color: #8a3db6;&quot;&gt;export default&lt;/span&gt; 작성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 코드가 없으면 다른 파일(App.jsx 등)에서 이 컴포넌트를 import로 불러올 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;3. 컴포넌트에 CSS 스타일 적용하기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;리액트에서는 컴포넌트 단위로 구조(JSX)와 스타일(CSS)을 세트로 관리하는 것이 일반적입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15&quot;&gt;① 전용 CSS 파일 생성 (Hello.css)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hello.jsx 파일과 같은 폴더에 Hello.css 파일을 만들고 스타일 코드를 작성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17&quot;&gt;② 컴포넌트에서 CSS 연결 (Hello.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;18&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 최상단에&lt;span style=&quot;color: #8a3db6;&quot;&gt; import './Hello.css&lt;/span&gt;'를 선언하여 스타일 파일을 불러옵니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0&quot;&gt;주의:&lt;/b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;태그에 클래스명을 부여할 때는 HTML의 class 대신 className을 사용해야 합니다.&lt;/span&gt; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;(자바스크립트의 예약어인 class와의 충돌을 피하기 위한 리액트 문법입니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19&quot;&gt;③ 메인 파일에서는 구조만 호출 (App.jsx)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;App.jsx에서 &amp;lt;Hello /&amp;gt;를 호출하면, Hello.jsx 내부에 연결된 CSS가 자동으로 함께 적용되어 화면에 출력됩니다.&lt;br /&gt;&lt;b&gt;메인 파일에서 별도의 디자인 코드를 추가할 필요가 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;4.리액트 부품 조립의 전체 흐름도&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;우리가 만들 파일들이 서로 어떻게 연결되는지 한눈에 보여주는 지도입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; data-path-to-node=&quot;6&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;단계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;역할 (비유)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;파일명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;하는 일&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0,0&quot;&gt;1단계&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,1,1,0&quot;&gt;  옷장&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,1,2,0&quot;&gt;Hello.css&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,1,3,0&quot;&gt;글자색, 배경색 등 부품의 디자인(CSS)을 적어둡니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0,0&quot;&gt;2단계&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,2,1,0&quot;&gt;  도장(부품) 조각하기&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,2,2,0&quot;&gt;Hello.jsx&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,2,3,0&quot;&gt;화면에 보여줄 뼈대를 만들고, 옷장(CSS)을 가져와 입힙니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,3,0,0&quot;&gt;3단계&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,3,1,0&quot;&gt;  다이어리에 도장 찍기&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,3,2,0&quot;&gt;App.jsx&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;6,3,3,0&quot;&gt;만들어진 도장을 가져와서 실제 메인 화면에 쾅쾅 찍어줍니다.&lt;br /&gt;실질적인 화면에 보여지는 역활을 합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size23&quot;&gt;  (1) 도장(부품) 조각하고 옷 입히기 (Hello.jsx)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;먼저 &quot;안녕!&quot;이라고 쓰인 예쁜 박스를 하나 만들어 보겠습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQ6wM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;Hello.jsx&lt;/div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import './Hello.css'        // ① 내 전용 옷장(CSS) 가져오기

const Hello = () =&amp;gt; {       // ② 'Hello'라는 이름의 도장(컴포넌트) 만들기 시작!
  return (                  // ③ 도장을 찍었을 때 화면에 나올 모양
    &amp;lt;div className=&quot;box&quot;&amp;gt;   // ④ 대장 상자 (이름표: box)
      &amp;lt;h1&amp;gt;안녕!&amp;lt;/h1&amp;gt;        // ⑤ 제일 큰 제목
      &amp;lt;p&amp;gt;반가워요  &amp;lt;/p&amp;gt;    // ⑥ 작은 설명 글
    &amp;lt;/div&amp;gt;
  )
}

export default Hello        // ⑦ &quot;이 도장 다 만들었으니 밖에서 가져다 써도 돼!&quot; 허락하기
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size20&quot;&gt;  태그 및 코드 상세 설명&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;import './Hello.css'&lt;/b&gt;: &quot;이 파일과 같은 폴더(.)에 있는 Hello.css 가져오기.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;const Hello = () =&amp;gt; { }&lt;/b&gt;: 화살표 함수라고 부르는 문법입니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 쉽게 말해 &quot;지금부터 &lt;b data-index-in-node=&quot;57&quot; data-path-to-node=&quot;12,1,0&quot;&gt;Hello&lt;/b&gt;라는 이름의 요리 레시피(부품)를 정의할게!&quot;라는 선언입니다. (첫 글자는 무조건 대문자!)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,2,0&quot;&gt;return ( )&lt;/b&gt;: &quot;이 괄호 안의 내용을 최종적으로 화면에 내보내 줘!&quot;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,3,0&quot;&gt;&amp;lt;div className=&quot;box&quot;&amp;gt;&lt;/b&gt;: 투명한 묶음 상자(&amp;lt;div&amp;gt;)입니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;CSS(옷장)에서 이 상자를 꾸밀 수 있도록 box라는 이름표(className)를 달아주었습니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; className은 HTML의 class와 똑같은 역할입니다!&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,4,0&quot;&gt;&amp;lt;h1&amp;gt;과 &amp;lt;p&amp;gt;&lt;/b&gt;: 제목(Heading)을 뜻하는 굵은 글씨 태그, 그리고 문단(Paragraph)을 뜻하는 일반 글씨 태그입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,5,0&quot;&gt;export default Hello&lt;/b&gt;: 다 만든 부품을 서랍(파일) 밖으로 내보내는 필수 마무릿줄입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;  (2). 부품을 예쁘게 꾸며줄 옷장 (Hello.css)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;방금 만든 도장(컴포넌트)에 입힐 디자인 코드입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQ7AM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CSS&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;/* Hello.jsx에서 className=&quot;box&quot;라고 이름표를 붙인 녀석을 꾸밉니다 */
.box {                     
  padding: 20px;          /* 상자 안쪽 여백을 20px만큼 빵빵하게 줍니다 */
  background: #f3e8ff;    /* 배경색을 연한 보라색으로 칠합니다 */
  border-radius: 12px;    /* 상자 모서리를 둥글게 깎아줍니다 */
  text-align: center;     /* 상자 안의 글자들을 가운데로 정렬합니다 */
}

/* box 상자 안에 있는 큰 제목(h1)만 특별히 꾸밉니다 */
.box h1 { 
  color: #aa3bff;         /* 글자색을 진한 보라색으로 바꿉니다 */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-path-to-node=&quot;17&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;17,0&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b data-index-in-node=&quot;3&quot; data-path-to-node=&quot;17,0&quot;&gt;주의:&lt;/b&gt; 리액트 파일 안이 아니라, 반드시 별도의 .css 확장자를 가진 파일에 적어야 합니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;  (3) 메인 화면에 부품 조립하기 (App.jsx)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;이제 옷까지 완벽하게 입은 Hello 도장을 가져와서 진짜 화면(App)에 출력하기&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQ7QM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import Hello from './components/Hello.jsx'   // ① 서랍에서 Hello 도장 꺼내오기

const App = () =&amp;gt; {             // ② 전체 웹사이트의 메인 화면 시작!
  return (
    &amp;lt;&amp;gt;                          // ③ 투명 포장지 (화면에 박스 흔적 안 남기기)
      &amp;lt;Hello /&amp;gt;                 // ④ 첫 번째 도장 쾅!
      &amp;lt;Hello /&amp;gt;                 // ⑤ 두 번째 도장 쾅! (재사용)
    &amp;lt;/&amp;gt;
  )
}

export default App              // ⑥ 메인 화면도 바깥으로 내보내기
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size20&quot;&gt;  태그 및 코드 상세 설명&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0,0&quot;&gt;import Hello from '...'&lt;/b&gt;: 지정된 경로 폴더(components) 안에 있는 Hello.jsx 파일을 현재 파일로 가져와서 Hello라는 이름으로 쓰겠다는 뜻입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,1,0&quot;&gt;&amp;lt;&amp;gt;&amp;lt;/&amp;gt; (Fragment)&lt;/b&gt;: 리액트의 &quot;반드시 하나의 태그로 감싸야 한다&quot;는 규칙을 지키면서도, 화면에 불필요한 &amp;lt;div&amp;gt; 상자를 만들지 않게 해주는 '투명 랩'입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,2,0&quot;&gt;&amp;lt;Hello /&amp;gt;&lt;/b&gt;: 우리가 정성껏 만든 도장(컴포넌트)을 드디어 사용하는 모습입니다! &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;빈 태그 닫기 규칙에 따라 끝에 꼭 슬래시(/)를 넣어주어야 합니다. &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 이렇게 한 줄만 적으면 그 자리에 방금 만든 보라색 박스가 통째로 그려집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;25&quot; data-ke-size=&quot;size23&quot;&gt; ️ (4) 예상 그림 (실제 브라우저 결과 화면)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;위의 모든 코드를 합쳐서 컴퓨터(브라우저)에서 실행하면?&lt;/p&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;짜잔! 아래와 같이 예쁜 보라색 상자가 메인 화면에 두 번 연달아 찍혀 나옵니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQ7gM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;Plaintext&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt; ┌──────────────────────────────────────────────────┐
 │   내 메인 웹사이트 (App.jsx)                     │
 ├──────────────────────────────────────────────────┤
 │                                                  │
 │   ╭──────────────────────────────────────────╮   │
 │   │                                          │   │
 │   │                안녕! (진한 보라색)         │   │  &amp;lt;-- 첫 번째 &amp;lt;Hello /&amp;gt;
 │   │             반가워요                    │   │
 │   │                                          │   │
 │   ╰──────────────────────────────────────────╯   │
 │                                                  │
 │   ╭──────────────────────────────────────────╮   │
 │   │                                          │   │
 │   │                안녕! (진한 보라색)         │   │  &amp;lt;-- 두 번째 &amp;lt;Hello /&amp;gt;
 │   │             반가워요                    │   │
 │   │                                          │   │
 │   ╰──────────────────────────────────────────╯   │
 │                                                  │
 └──────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/9</guid>
      <comments>https://sojsoj.tistory.com/9#entry9comment</comments>
      <pubDate>Fri, 10 Jul 2026 16:44:55 +0900</pubDate>
    </item>
    <item>
      <title>9일차_ React_(App.jsx구조)</title>
      <link>https://sojsoj.tistory.com/8</link>
      <description>&lt;h3 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;⚛️ 1. React(리액트)란 무엇일까요?&lt;/h3&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;리액트를 한마디로 정의하면 &quot;웹사이트를 레고 블록 조립하듯 뚝딱뚝딱 만드는 도구&quot;입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;예전에는 웹사이트(집)를 만들 때 벽돌을 처음부터 끝까지 하나하나 쌓아 올렸다면, 리액트는 미리 만들어둔 '블록(컴포넌트)'들을 가져다 끼우기만 하면 됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;게다가 블록 안의 글자(데이터)만 쓱 바꾸면, 리액트가 알아서 화면을 고쳐줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1783667213565&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[개별 레고 블록들]                 [완성된 웹사이트 화면]
 ┌───────────┐                     ┌────────────────────┐
 │   검색창 │                     │   검색창          │
 └───────────┘                     ├─────────┬──────────┤
 ┌───────────┐      조립(React)    │         │          │
 │   프로필 │    ────────────▶    │  프로필│  ️사진 1 │
 └───────────┘                     │         │          │
 ┌───────────┐                     ├─────────┼──────────┤
 │  ️ 사진 1 │                     │ ⚙️ 설정 │  ️사진 2 │
 └───────────┘                     └─────────┴──────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;  2. JSX : 자바스크립트 안에 HTML 쓰기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;리액트에서는 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;12&quot;&gt;JSX&lt;/b&gt;라는 아주 특별한 문법을 씁니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말해 &quot;자바스크립트(JS) 파일 안에서 HTML 뼈대를 마음껏 쓸 수 있게 해주는 규칙&quot;입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;HTML과 거의 똑같이 생겼지만, 딱 &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;13&quot;&gt;3가지 작은 규칙&lt;/b&gt;만 주의하시면 됩니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size20&quot;&gt;  HTML과 JSX의 결정적 차이점 3가지&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 147px;&quot; border=&quot;1&quot; data-path-to-node=&quot;15&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 12.907%; height: 21px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.186%; height: 21px;&quot;&gt;&lt;b&gt;기존 HTML 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%; height: 21px;&quot;&gt;&lt;b&gt;리액트 JSX 방식 (✨)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.5116%; height: 21px;&quot;&gt;&lt;b&gt;왜 다를까요?&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 12.907%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0,0&quot;&gt;① 이름표&lt;br /&gt;&amp;nbsp; &amp;nbsp; (클래스)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.186%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,1,1,0&quot;&gt;&amp;lt;div class=&quot;box&quot;&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,1,2,0&quot;&gt;&amp;lt;div &lt;span style=&quot;color: #ee2323;&quot;&gt;className&lt;/span&gt;=&quot;box&quot;&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.5116%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,1,3,0&quot;&gt;자바스크립트에 이미 class라는 단어가 있어서 겹치지 않게 피한 거예요!&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 12.907%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,2,0,0&quot;&gt;② 데이터 넣기&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.186%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,2,1,0&quot;&gt;&amp;lt;p&amp;gt;수강생님 환영합니다&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,2,2,0&quot;&gt;&amp;lt;p&amp;gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;{name}&lt;/span&gt;님 환영합니다&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.5116%; height: 42px;&quot;&gt;&lt;u&gt;&lt;span data-path-to-node=&quot;15,2,3,0&quot;&gt;{ } 중괄호를 열면 그 안에 'JS 값이나 계산식'을 넣을 수 있어요.&lt;/span&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 12.907%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,3,0,0&quot;&gt;③ 태그 닫기&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.186%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,3,1,0&quot;&gt;&amp;lt;img src=&quot;a.png&quot;&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,3,2,0&quot;&gt;&amp;lt;img src=&quot;a.png&quot;&lt;span style=&quot;color: #ee2323;&quot;&gt; /&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.5116%; height: 42px;&quot;&gt;&lt;span data-path-to-node=&quot;15,3,3,0&quot;&gt;리액트는 아주 엄격해서 짝이 없는&lt;span style=&quot;color: #ee2323;&quot;&gt; 빈 태그는 끝에 꼭 /로 닫아&lt;/span&gt;줘야 해요.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; { }&lt;/span&gt; 중괄호는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&quot;자, 지금부터 자바스크립트 값이 들어간다!&quot;&lt;/span&gt; 라고 알려주는 마법의 주머니라고 생각하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수, 숫자 계산, 함수 등 뭐든 들어갈 수 있어요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. 실전 예제와 예상 화면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트(JSX) 코드&lt;/p&gt;
&lt;pre id=&quot;code_1783667705668&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
  const name = &quot;수강생&quot;
  const age = 20

  return (
    &amp;lt;div className=&quot;box&quot;&amp;gt; 
      &amp;lt;h1&amp;gt;안녕, {name}!&amp;lt;/h1&amp;gt;      
      &amp;lt;p&amp;gt;{1 + 1}&amp;lt;/p&amp;gt;                   
      &amp;lt;img src=&quot;a.png&quot; alt=&quot;이미지&quot; /&amp;gt; 
      &amp;lt;p&amp;gt;{name}님은 {age}살, 내년엔 {age + 1}살&amp;lt;/p&amp;gt; 
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[예상 그림] 실제 인터넷 창에 뜨는 결과&lt;/p&gt;
&lt;pre id=&quot;code_1783667786155&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──────────────────────────────────────────────────┐
 │   내 첫 리액트 웹사이트                         │
 ├──────────────────────────────────────────────────┤
 │                                                  │
 │  안녕, 수강생!                                   │
 │                                                  │
 │  2                                               │
 │                                                  │
 │  [  ️ a.png 이미지 사진이 뜨는 자리 ]            │
 │                                                  │
 │  수강생님은 20살, 내년엔 21살                    │
 │                                                  │
 └──────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구간별 이해하기&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  [1. ] 변수 선언 구역 (재료 준비)&lt;/p&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {    // 1. App이라는 요리사(컴포넌트)를 만들게요!
  const name = &quot;수강생&quot; // 2. 'name'이라는 상자에 &quot;수강생&quot; 글자를 담아둡니다 (재료 1)
  const age = 20       // 3. 'age'이라는 상자에 20이라는 숫자를 담아둡니다 (재료 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; ️ [2. ] return ( ) 구역 (실제 화면에 보여지는 접시)&lt;/p&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;  return (
    &amp;lt;div className=&quot;box&quot;&amp;gt; 
      &amp;lt;h1&amp;gt;안녕, {name}!&amp;lt;/h1&amp;gt;      
      &amp;lt;p&amp;gt;{1 + 1}&amp;lt;/p&amp;gt;                   
      &amp;lt;img src=&quot;a.png&quot; alt=&quot;이미지&quot; /&amp;gt; 
      &amp;lt;p&amp;gt;{name}님은 {age}살, 내년엔 {age + 1}살&amp;lt;/p&amp;gt; 
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;역할:&lt;/b&gt; &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;11,0,0&quot;&gt;여기서부터가 진짜 인터넷 화면에 그려지는 코드입니다!&lt;/b&gt; * 요리사가 준비한 재료들을 예쁜 그릇(&amp;lt;div&amp;gt;, &amp;lt;h1&amp;gt;, &amp;lt;p&amp;gt;)에 담아서 손님(사용자)에게 내놓는(return) 과정입니다.&lt;/li&gt;
&lt;li&gt;컴퓨터는 이 return ( ) 안에 적힌 코드를 읽어서 우리 눈에 보이는 웹사이트 화면을 만들어 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSS 적용할 때 &amp;lt;div&amp;gt;를 쓴다.&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;HTML이나 리액트에서 &amp;lt;div&amp;gt;는 아무 기능이 없는 '투명한 빈 상자(Box)'입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;글자나 이미지를 이 상자 안에 예쁘게 묶어두고, 상자에 이름표(className)를 붙여서 CSS로 스타일을 꾸밉니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;코드 분석:&lt;/b&gt; &amp;lt;div className=&quot;box&quot;&amp;gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;의미:&lt;/b&gt; &quot;지금부터 이 상자 안에 들어가는 모든 요소들을 'box'라는 이름의 구역으로 묶을게! 그리고 CSS 파일에 가서 &lt;b data-index-in-node=&quot;68&quot; data-path-to-node=&quot;16,1,0&quot;&gt;.box&lt;/b&gt;한테 예쁜 배경색이랑 테두리를 달아줘!&quot;라고 명령하는 것입니다. 구역을 나누고 디자인을 입힐 때 &amp;lt;div&amp;gt;는 가장 많이 쓰이는 필수 태그입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;lt;&amp;gt;&amp;lt;/&amp;gt; (프래그먼트) 배우기&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-path-to-node=&quot;20&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;20,0&quot; data-ke-size=&quot;size16&quot;&gt;  &lt;b data-index-in-node=&quot;3&quot; data-path-to-node=&quot;20,0&quot;&gt;리액트의 대원칙:&lt;/b&gt; return ( ) 안의 모든 코드들은 반드시 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;20,0&quot;&gt;커다란 하나의 상자(태그) 안에 다 감싸져 있어야 한다!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;만약 아래처럼 대장 상자(&amp;lt;div&amp;gt;) 없이 알맹이들만 덜렁 내놓으면 리액트는 &quot;누가 대장 상자야?!&quot; 하면서 에러를 냅니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQnAM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ❌ 에러 발생! 감싸주는 대장 상자가 없어요.
return (
  &amp;lt;h1&amp;gt;안녕!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;반가워&amp;lt;/p&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;이때 굳이 CSS 디자인을 줄 것도 아니고,&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;화면 구조상 무의미한 &amp;lt;div&amp;gt; 상자를 또 만들고 싶지 않을 때 쓰는 마법의 투명 상자가 바로 &lt;b data-index-in-node=&quot;78&quot; data-path-to-node=&quot;23&quot;&gt;&amp;lt;&amp;gt; &amp;lt;/&amp;gt; (React Fragment)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size20&quot;&gt; ️ &amp;lt;&amp;gt;&amp;lt;/&amp;gt;를 사용한 코드 예시&lt;/h4&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwis1PuBxMeVAxUAAAAAHQAAAAAQnQM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;JavaScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;  {/* 무의미한 div 대신 투명 상자로 시작! */}
      &amp;lt;h1&amp;gt;안녕!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;반가워&amp;lt;/p&amp;gt;
    &amp;lt;/&amp;gt; {/* 투명 상자 끝! */}
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-path-to-node=&quot;26&quot; data-ke-size=&quot;size20&quot;&gt;  [도표] &amp;lt;div&amp;gt; vs &amp;lt;&amp;gt;&amp;lt;/&amp;gt; 차이점 정리&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;27&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;코드 모양&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 브라우저(HTML) 결과&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;언제 쓰나요?&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,1,0,0&quot;&gt;① &amp;lt;div&amp;gt; 상자&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,1,0&quot;&gt;&amp;lt;div&amp;gt; ... &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,2,0&quot;&gt;&amp;lt;div&amp;gt; 상자가 화면 구조에 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;27,1,2,0&quot;&gt;실제로 남음&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,3,0&quot;&gt;테두리, 배경색 등 &lt;b data-index-in-node=&quot;11&quot; data-path-to-node=&quot;27,1,3,0&quot;&gt;CSS 디자인을 직접 입혀야 할 때&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,2,0,0&quot;&gt;② &amp;lt;&amp;gt;&amp;lt;/&amp;gt; 투명 상자&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,1,0&quot;&gt;&amp;lt;&amp;gt; ... &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,2,0&quot;&gt;감싸는 태그 없이 알맹이만 깔끔하게 남음&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,3,0&quot;&gt;디자인은 필요 없고, 리액트의 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;27,2,3,0&quot;&gt;&quot;하나로 감싸라&quot;는 규칙만 통과하고 싶을 때&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/8</guid>
      <comments>https://sojsoj.tistory.com/8#entry8comment</comments>
      <pubDate>Fri, 10 Jul 2026 16:26:31 +0900</pubDate>
    </item>
    <item>
      <title>7일차_ _JavaScript 문법 /  7가지 데이터 타입</title>
      <link>https://sojsoj.tistory.com/7</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript 문법&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법의미코드에서 사용하는 형태쉽게 기억하기&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;let&lt;/td&gt;
&lt;td&gt;값이 바뀔 수 있는 변수&lt;/td&gt;
&lt;td&gt;let myName = nameInput.value;&lt;/td&gt;
&lt;td&gt;메모장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;const&lt;/td&gt;
&lt;td&gt;바뀌지 않는 변수&lt;/td&gt;
&lt;td&gt;const out = document.querySelector(&quot;#out&quot;);&lt;/td&gt;
&lt;td&gt;이름표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;=&lt;/td&gt;
&lt;td&gt;오른쪽 값을 왼쪽 변수에 저장&lt;/td&gt;
&lt;td&gt;let myName = nameInput.value;&lt;/td&gt;
&lt;td&gt;상자에 넣기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;document&lt;/td&gt;
&lt;td&gt;현재 웹페이지&lt;/td&gt;
&lt;td&gt;document.querySelector(&quot;#name&quot;)&lt;/td&gt;
&lt;td&gt;교실 전체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;querySelector()&lt;/td&gt;
&lt;td&gt;HTML 요소 찾기&lt;/td&gt;
&lt;td&gt;document.querySelector(&quot;#greet&quot;)&lt;/td&gt;
&lt;td&gt;물건 찾기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.value&lt;/td&gt;
&lt;td&gt;입력창 안의 값&lt;/td&gt;
&lt;td&gt;nameInput.value&lt;/td&gt;
&lt;td&gt;사용자가 쓴 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.textContent&lt;/td&gt;
&lt;td&gt;글자 바꾸기&lt;/td&gt;
&lt;td&gt;out.textContent = &quot;안녕!&quot;;&lt;/td&gt;
&lt;td&gt;칠판 글씨 바꾸기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;addEventListener()&lt;/td&gt;
&lt;td&gt;이벤트가 발생하면 실행&lt;/td&gt;
&lt;td&gt;button.addEventListener(&quot;click&quot;, () =&amp;gt; {})&lt;/td&gt;
&lt;td&gt;버튼을 누르면 행동하기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;click&quot;&lt;/td&gt;
&lt;td&gt;이벤트 중 &quot;클릭&quot; 이벤트&lt;/td&gt;
&lt;td&gt;&quot;click&quot;&lt;/td&gt;
&lt;td&gt;마우스로 누르기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;` `&lt;/td&gt;
&lt;td&gt;템플릿 리터럴&lt;/td&gt;
&lt;td&gt;`안녕, ${myName}!`&lt;/td&gt;
&lt;td&gt;문자열 안에 변수 넣기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;${}&lt;/td&gt;
&lt;td&gt;변수 값을 문자열에 삽입&lt;/td&gt;
&lt;td&gt;`안녕, ${myName}!`&lt;/td&gt;
&lt;td&gt;빈칸에 값 채우기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 타입(Data Type) &amp;mdash; 상자에 담는 값의 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수(상자) 안에는 아무 값이나 넣을 수 있는 것 같지만, &lt;b&gt;값에도 종류(데이터 타입)&lt;/b&gt; 가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값도 글자, 숫자, 참/거짓 등 여러 종류가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 타입에 따라 사용할 수 있는 기능과 작성 방법이 조금씩 달라집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;값의 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 타입의미코드에서 사용하는 형태쉽게 기억하기&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;글자(문자열)&lt;/td&gt;
&lt;td&gt;let name = &quot;Bob&quot;;&lt;/td&gt;
&lt;td&gt;글자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;숫자&lt;/td&gt;
&lt;td&gt;let age = 10;&lt;/td&gt;
&lt;td&gt;계산하는 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;참 또는 거짓&lt;/td&gt;
&lt;td&gt;let isLogin = true;&lt;/td&gt;
&lt;td&gt;스위치(ON/OFF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;undefined&lt;/td&gt;
&lt;td&gt;아직 값을 넣지 않음&lt;/td&gt;
&lt;td&gt;let x;&lt;/td&gt;
&lt;td&gt;빈 상자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;일부러 비워 둠&lt;/td&gt;
&lt;td&gt;let user = null;&lt;/td&gt;
&lt;td&gt;비워두기로 결정한 상자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;여러 값을 순서대로 저장&lt;/td&gt;
&lt;td&gt;let list = [1, &quot;Bob&quot;, 10];&lt;/td&gt;
&lt;td&gt;여러 칸이 있는 서랍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;td&gt;여러 정보를 하나로 묶음&lt;/td&gt;
&lt;td&gt;let user = { name: &quot;Bob&quot;, age: 10 };&lt;/td&gt;
&lt;td&gt;정보 묶음(가방)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;하나씩 알아보기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. String (문자열)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글자를 저장하는 데이터 타입입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글자는 반드시 &lt;b&gt;따옴표(&quot; 또는 ')&lt;/b&gt; 로 감싸야 합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let name = &quot;Bob&quot;;
let city = &quot;Seoul&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;out.textContent = &quot;안녕하세요!&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Number (숫자)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 저장하는 데이터 타입입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 &lt;b&gt;따옴표를 사용하지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let age = 20;
let score = 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계산 가능&lt;/h3&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;let a = 10;
let b = 5;

console.log(a + b);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;15
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Boolean (참 / 거짓)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 프로그램을 만들다 보면 &lt;b&gt;&quot;맞다&quot; 또는 &quot;아니다&quot;&lt;/b&gt; 처럼 &lt;b&gt;두 가지 상태만 저장&lt;/b&gt;해야 하는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인했나요?&lt;/li&gt;
&lt;li&gt;성인인가요?&lt;/li&gt;
&lt;li&gt;전등이 켜져 있나요?&lt;/li&gt;
&lt;li&gt;숙제를 했나요?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문들은 모두 &lt;b&gt;예 / 아니오&lt;/b&gt;로 대답할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 데이터 타입이 &lt;b&gt;Boolean&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Boolean은 딱 두 가지 값만 있습니다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean은 &lt;b&gt;true 또는 false 둘 중 하나만 저장할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제 1. 전등&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전등을 생각해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전등이 켜져 있을 때&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;let light = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뜻&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;전등이 켜져 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전등이 꺼져 있을 때&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;let light = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뜻&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;전등이 꺼져 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로그램에서는 왜 사용할까요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;예&quot;

또는

&quot;아니오&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 판단해서 다음 행동을 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 로그인 버튼을 눌렀다고 해봅시다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let isLogin = true;

if (isLogin) {
    console.log(&quot;환영합니다!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 순서&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;로그인했나요?

&amp;darr;

예(true)

&amp;darr;

환영합니다!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let isLogin = false;

if (isLogin) {
    console.log(&quot;환영합니다!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;아무것도 실행되지 않음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;로그인하지 않았기 때문입니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;코드에서 사용하는 형태&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;예 또는 아니오를 저장하는 값&lt;/td&gt;
&lt;td&gt;let isLogin = true;&lt;/td&gt;
&lt;td&gt;ON / OFF 스위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;예, 맞다, 켜짐&lt;/td&gt;
&lt;td&gt;let light = true;&lt;/td&gt;
&lt;td&gt;✅ 켜짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;아니오, 아니다, 꺼짐&lt;/td&gt;
&lt;td&gt;let light = false;&lt;/td&gt;
&lt;td&gt;❌ 꺼짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. undefined&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 만들었지만 아직 아무 값도 넣지 않은 상태입니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;let x;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;console.log(x);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. null&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비어 있다는 것을 &lt;b&gt;개발자가 일부러 표시&lt;/b&gt;하는 값입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;let user = null;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;회원 정보

&amp;darr;

아직 없음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라는 뜻입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쉽게 기억하기&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;일부러 비워 둔 상자
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Array (배열) [&amp;nbsp; ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 값을 하나의 변수에 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;포도&quot;];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;let numbers = [1, 2, 3];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;섞어서도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;let data = [1, &quot;Bob&quot;, true];
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쉽게 기억하기&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[   ][   ][   ]

&amp;darr;

여러 칸이 있는 서랍
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Object (객체) { }&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 있는 여러 정보를 하나로 묶는 데이터 타입입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;let user = {
    name: &quot;Bob&quot;,
    age: 20
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름&lt;/li&gt;
&lt;li&gt;나이&lt;/li&gt;
&lt;li&gt;주소&lt;/li&gt;
&lt;li&gt;전화번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 여러 정보를 함께 저장할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;값 꺼내기&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;console.log(user.name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쉽게 기억하기&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;학생

이름 : Bob

나이 : 20

학년 : 3

&amp;darr;

학생 한 명의 정보 묶음
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;데이터 타입 한눈에 보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 타입예시어떤 값인가?&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&quot;Bob&quot;&lt;/td&gt;
&lt;td&gt;글자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;참/거짓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;undefined&lt;/td&gt;
&lt;td&gt;let x;&lt;/td&gt;
&lt;td&gt;아직 값 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;일부러 비움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;[&quot;사과&quot;, &quot;배&quot;]&lt;/td&gt;
&lt;td&gt;여러 값을 순서대로 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object&lt;/td&gt;
&lt;td&gt;{ name: &quot;Bob&quot;, age: 20 }&lt;/td&gt;
&lt;td&gt;여러 정보를 하나로 묶음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/7</guid>
      <comments>https://sojsoj.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 9 Jul 2026 16:32:36 +0900</pubDate>
    </item>
    <item>
      <title>6일차 _JavaScript 기초</title>
      <link>https://sojsoj.tistory.com/6</link>
      <description>&lt;h1&gt;JavaScript 보기 전 알아야 하는 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 &lt;b&gt;HTML을 새로 만드는 언어가 아니라, 이미 만들어진 HTML을 찾아서 제어하는 언어&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 HTML에 아래와 같은 코드가 있다고 가정해 봅시다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;h1 id=&quot;title&quot;&amp;gt;안녕하세요&amp;lt;/h1&amp;gt;

&amp;lt;button id=&quot;btn&quot;&amp;gt;확인&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 이 요소들을 직접 만들지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;HTML에서 title이라는 이름표(id)를 가진 요소를 찾아와.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 명령합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아온 요소를 JavaScript 안에서 사용할 수 있도록 변수(또는 상수)에 저장한 후 원하는 기능을 추가하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;HTML
        &amp;darr;
JavaScript가 HTML 요소를 찾는다.
        &amp;darr;
찾은 요소를 변수(상수)에 저장한다.
        &amp;darr;
저장된 요소를 이용해서 글자를 바꾸거나,
색을 바꾸거나,
클릭 이벤트를 추가한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;const란?&lt;/h1&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const title = document.querySelector(&quot;#title&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 const부터 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const는 &lt;b&gt;상수(Constant)를 만드는 키워드&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수는 &lt;b&gt;한 번 저장한 값을 다른 값으로 다시 바꾸지 않는 변수&lt;/b&gt;라고 생각하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;html이나 css  a 인것을 JS에서 a로 하기 위한  코드&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;document는 무엇일까?&lt;/h2&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 열려 있는 &lt;b&gt;웹페이지 전체(HTML 문서)&lt;/b&gt;를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 HTML을 하나의 document(문서)로 관리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;querySelector()는 무엇일까?&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;document.querySelector(&quot;#title&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;는&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;document 안에서 원하는 요소를 찾는 명령입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;쉽게 번역하면&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;웹페이지(document) 안에서

id가 title인 요소를 찾아라.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;라는 뜻입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;#은 왜 붙을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에서는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;id=&quot;title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라고 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 JavaScript에서 id를 찾을 때는&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;&quot;#title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 **#**을 붙입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 전체를 해석해 보기&lt;/h2&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const title = document.querySelector(&quot;#title&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄씩 해석하면&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;const
&amp;darr;
title라는 상수를 만든다.

document
&amp;darr;
현재 웹페이지에서

querySelector(&quot;#title&quot;)
&amp;darr;
id가 title인 요소를 찾는다.

=
&amp;darr;
찾은 요소를 title 상자에 저장한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 해석&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;웹페이지에서 id가 title인 HTML 요소를 찾아 title이라는 상수에 저장한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;또 다른 예제&lt;/h1&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const a = document.querySelector(&quot;#a&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드도 같은 원리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;a&quot;&amp;gt;안녕하세요&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const a = document.querySelector(&quot;#a&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해석&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTML에서 id가 a인 요소를 찾아 JavaScript에서 a라는 상수에 저장한다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;HTML의 요소를 JavaScript에서 사용할 수 있도록 연결하는 과정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;addEventListener()는 무엇일까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 아래 코드를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document.querySelector(&quot;#calc&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보면 길어 보이지만, 작은 단위로 나누면 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① document.querySelector(&quot;#calc&quot;)&lt;/h2&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;document.querySelector(&quot;#calc&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;웹페이지에서

id가 calc인 요소를 찾는다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② addEventListener()&lt;/h2&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;.addEventListener()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 뜻은&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 요소에 이벤트를 등록한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이벤트(Event)란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떤 일이 발생하는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버튼 클릭&lt;/li&gt;
&lt;li&gt;키보드 입력&lt;/li&gt;
&lt;li&gt;마우스 이동&lt;/li&gt;
&lt;li&gt;화면 스크롤&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 이벤트입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ &quot;click&quot;&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;click&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭 이벤트를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자가 이 요소를 클릭하면
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라는 뜻입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;④ () =&amp;gt; { }&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;() =&amp;gt;&lt;/b&gt;&lt;/u&gt;&amp;nbsp; 에서 괄호 안에 아무것도 넣지 않는다. 의미는 &lt;b&gt;&lt;u&gt;바뀔거야&amp;nbsp; &lt;/u&gt;&amp;nbsp;{}로&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;() =&amp;gt; {

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 &lt;b&gt;화살표 함수(Arrow Function)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;{} 클릭했을 때 실행할 명령을 적는 공간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드 해석&lt;/h2&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document.querySelector(&quot;#calc&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서대로 해석하면&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;① HTML에서 id가 calc인 요소를 찾는다.

&amp;darr;

② 그 요소에 클릭 이벤트를 등록한다.

&amp;darr;

③ 사용자가 클릭하면

&amp;darr;

④ { } 안에 작성된 코드가 실행된다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HTML에서 id가 calc인 요소를 찾아, 클릭 이벤트를 등록하고, 사용자가 클릭하면 { } 안의 코드를 실행하라는 의미입니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;{ }의 상태로 바뀌는 것이 아니라, { } 안의 코드가 실행되는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표현이 JavaScript의 동작 원리를 가장 정확하게 설명합니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #121314; color: #bbbebf;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #8b949e;&quot;&gt;() =&amp;gt; &amp;nbsp;이것의 의미는 이것을 클릭하면 이것이된다 라고 해석됨 &amp;nbsp;정해진 양식으로 ()안에 무엇을 따로 입력할 필요는 없다. &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>성남 프론트엔드</category>
      <author>sojsoj</author>
      <guid isPermaLink="true">https://sojsoj.tistory.com/6</guid>
      <comments>https://sojsoj.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 6 Jul 2026 16:41:22 +0900</pubDate>
    </item>
  </channel>
</rss>