<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>인뽕 님의 개발일지</title>
    <link>https://bb0ng2.tistory.com/</link>
    <description>인뽕의 개발일지 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 5 Aug 2026 06:52:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>인뽕</managingEditor>
    <image>
      <title>인뽕 님의 개발일지</title>
      <url>https://tistory1.daumcdn.net/tistory/7375519/attach/cdac2d191c5245e99bf3562df1f97216</url>
      <link>https://bb0ng2.tistory.com</link>
    </image>
    <item>
      <title>0722 MVP, 스레드</title>
      <link>https://bb0ng2.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#Gemini&amp;nbsp;활용&amp;nbsp;꿀팁&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;'''
코드 붙여넣고,
&quot;이 코드 내부 실행 순서를 시뮬레이터로 만들어줘. 단, html로&quot;
설정은 Canvas로
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;MVP&lt;/b&gt;&lt;/i&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;#MVP&amp;nbsp;패턴&amp;nbsp;적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
MVP는 Model-View-Presenter의 약자로, GUI 프로그램의 코드를 역할에 따라 세 부분으로 
나누는 아키텍처입니다.
핵심 목적은 데이터, 화면, 로직을 분리하여 규모가 커져도 코드를 관리할 수 있게 만드는 것입니다.

MVP의 세가지 역할

구성 요소                       역할                      tkinter에서
Model                  데이터와 비즈니스 로직             순수 파이썬 클래스 (tkinter 모름)
View    `              화면 표시와 사용자 입력 받기        위젯 배치, 이벤트 전달
Presenter              Model과 View를 연결하는 중재자     입력 처리 후 Model 갱신, View 업데이트

핵심 규칙과 데이터 흐름

핵심 규칙은 &quot;View와 Model은 서로 직접 대화하지 않는다&quot; 입니다.
모든 흐름은 Presenter를 거칩니다.

[사용자] &amp;rarr; View &amp;rarr; Presenter &amp;rarr; Model
              &amp;darr;
[사용자] &amp;larr; View &amp;larr; Presenter

View는 스스로 판단하지 않고 사용자 동작을 Presenter에게 &quot;전달&quot;만 합니다.
Model은 tkinter를 전혀 모르는 순수한 파이썬일 뿐입니다.
Presenter는 Model과 View를 둘 다 알고 있으며, 흐름을 조율합니다.

MVP 적용 시 장점

화면(View) 없이도 데이터(Model)와 로직(Presenter)을 독립적으로 단위 테스트할 수 있습니다.
화면을 변경해도 로직은 그대로, 로직을 수정해도 화면은 그대로 둘 수 있어 유지보수가 편리합
니다.
디자이너는 화면에, 개발자는 로직에 집중할 수 있어 명확한 업무 분장과 협업이 가능합니다.
프로그램 기능이 늘어나도 역할별로 코드가 체계적으로 정리되어 확장성이 뛰어납니다.


'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;MVP&amp;nbsp;패턴으로&amp;nbsp;앱&amp;nbsp;만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;Model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;#Model은 import tkinter 가 없는 순수한 파이썬일 뿐입니다.
class DistanceModel:
    KM_PER_MILE = 1.609344

    def km_to_miles(self, km):
        return km / self.KM_PER_MILE
    def miles_to_km(self, miles):
        return miles * self.KM_PER_MILE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#View&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 위젯을 만들고, 사용자 동작을 Presenter에게 &quot;전달&quot;만 합니다. 스스로 판단 X

import tkinter as tk

class DistanceView(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.pack(padx=15, pady=15)

        self.entry = tk.Entry(self, width=15)
        self.entry.grid(row=0, column=0, columnspan=2, pady=5)

        self.km_btn = tk.Button(self, text=&quot;km &amp;rarr; 마일&quot;)
        self.km_btn.grid(row=1, column=0, padx=2)

        self.mile_btn = tk.Button(self, text=&quot;마일 &amp;rarr; km&quot;)
        self.mile_btn.grid(row=1, column=1, padx=2)

        self.result = tk.Label(self, text=&quot;값을 입력하세요&quot;, width=25)
        self.result.grid(row=2, column=0, columnspan=2, pady=10)

    def get_input(self):
        return self.entry.get()

    def show_result(self, text):
        self.result.config(text=text)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#Presenter&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;#Presenter는 Model과 View를 둘 다 알고 있으며, 흐름을 조율합니다.

class DistancePresenter:
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.view.km_btn.config(command=self.on_km_to_miles)
        self.view.mile_btn.config(command=self.on_miles_to_km)
    def _read_value(self):
        &quot;&quot;&quot;입력값을 숫자로 변환. 실패하면 None과 함께 안내.&quot;&quot;&quot;
        try:
            return float(self.view.get_input())
        except ValueError:
            self.view.show_result(&quot;숫자를 입력하세요&quot;)
            return None
    def on_km_to_miles(self):
        value = self._read_value()
        if value is not None:
            miles = self.model.km_to_miles(value)
            self.view.show_result(f&quot;{value} km = {miles:.2f} 마일&quot;)
    def on_miles_to_km(self):
        value = self._read_value()
        if value is not None:
            km = self.model.miles_to_km(value)
            self.view.show_result(f&quot;{value} 마일 = {km:.2f} km&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    presenter = DistancePresenter(DistanceModel(), DistanceView(root))
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;km-마일 변환 프로그램이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;213&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSiitK/dJMcaccQeOc/tZIA2SNst2tsrakf2a5lHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSiitK/dJMcaccQeOc/tZIA2SNst2tsrakf2a5lHK/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSiitK/dJMcaccQeOc/tZIA2SNst2tsrakf2a5lHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSiitK%2FdJMcaccQeOc%2FtZIA2SNst2tsrakf2a5lHK%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;213&quot; height=&quot;158&quot; data-origin-width=&quot;213&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;스레드&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
스레드란

프로그램을 실행하면 프로세스가 생기고, 그 안에서 실제로 코드를 실행하는 흐름이 스레드
(Thread) 입니다.
기본적으로 스레드는 하나(메인 스레드)지만, 필요하면 여러 개를 만들어 일을 나눌 수 있습니다.

스레드는 왜 필요한가?

스레드가 하나면 모든 일을 순서대로 처리합니다. 그래서 오래 걸리는 작업(읽기, 네트워크 등)을
 만나면 끝날 때까지 다른 일이 멈춥니다.
time.sleep , 파일
GUI에서 이 문제가 바로 '응답 없음' 입니다. 메인 스레드가 대기 작업에 묶이면 창 이동&amp;middot;버튼 클
릭에 반응하지 못합니다.
그래서 해결책은 역할을 나누는 것입니다.
메인 스레드 : 화면을 그리고 사용자 입력에 반응하는 일에 집중합니다.
작업 스레드 : 오래 걸리는 작업을 따로 맡아 처리합니다.
이렇게 하면 작업이 도는 동안에도 화면이 멈추지 않아 '응답 없음'을 피할 수 있습니다.

파이썬에서 스레드 구성 방법

파이썬은 표준 라이브러리인 
threading 모듈로 스레드를 다룹니다. 스레드를 만드는 방식은 크게 두 가지입니다.

함수를 넘기는 방식

가장 간단한 방법은 Thread 객체를 만들면서 실행할 함수를 target 으로 지정하는 것입니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import threading
import time

def work():
    for i in range(5):
        print(f&quot;작업 중 {i}&quot;)
        time.sleep(1)

t = threading.Thread(target=work)
t.start()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초동안 대기하고 print하는 스레드를 만든 코드이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;101&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u8FTv/dJMcaccQeWp/Kz0z37UEmuImJ4HGCyMkZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u8FTv/dJMcaccQeWp/Kz0z37UEmuImJ4HGCyMkZk/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u8FTv/dJMcaccQeWp/Kz0z37UEmuImJ4HGCyMkZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu8FTv%2FdJMcaccQeWp%2FKz0z37UEmuImJ4HGCyMkZk%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;101&quot; height=&quot;130&quot; data-origin-width=&quot;101&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
Thread를 상속하는 방식

좀 더 구조적인 방법은 threading.Thread 를 상속한 클래스를 만들고 run 메서드를 재정의하 
는 것입니다. 
스레드가 다룰 데이터나 상태가 많아 객체지향적으로 설계할 때 적합합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import threading
import time

class Worker(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.worker_name = name
    def run(self):
        for i in range(5):
            time.sleep(1)
            print(f&quot;{self.worker_name}: {i}&quot;)

w = Worker(&quot;작업자1&quot;)
w.start()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;threading.Thread를 상속받고, run함수를 오버라이딩한 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbikD0/dJMcah6nmaF/TDHxk8ASiONVxHTfkUkeF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbikD0/dJMcah6nmaF/TDHxk8ASiONVxHTfkUkeF1/img.png&quot; data-alt=&quot;Thread의 run함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbikD0/dJMcah6nmaF/TDHxk8ASiONVxHTfkUkeF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbikD0%2FdJMcah6nmaF%2FTDHxk8ASiONVxHTfkUkeF1%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;656&quot; height=&quot;464&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread의 run함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;93&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gzh3H/dJMcaic9rgX/e1KvKtA0dxqcNPT1aJvnk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gzh3H/dJMcaic9rgX/e1KvKtA0dxqcNPT1aJvnk1/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gzh3H/dJMcaic9rgX/e1KvKtA0dxqcNPT1aJvnk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGzh3H%2FdJMcaic9rgX%2Fe1KvKtA0dxqcNPT1aJvnk1%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;93&quot; height=&quot;121&quot; data-origin-width=&quot;93&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
이제 같은 카운트다운 프로그램을 두 가지 방식으로 만들어, 화면이 멈추는 경우와 멈추지 않는
경우를 직접 비교해 보겠습니다.
두 코드는 10부터 0까지 1초 간격으로 숫자를 줄여 표시하고, 마지막에 &quot;끝!&quot;을 보여준다는 점에
서 동일합니다.
차이는 그 작업을 누가 처리하느냐에 있습니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#메인 스레드에서 처리

import tkinter as tk
import time
class CountdownApp:
    def __init__(self, root):
        self.root = root
        self.label = tk.Label(root, text=&quot;10&quot;, font=(&quot;Arial&quot;, 60))
        self.label.pack(expand=True)
        tk.Button(root, text=&quot;시작&quot;, command=self.start).pack(pady=10)

    def start(self):
        for i in range(10, -1, -1):
            self.label.config(text=str(i))
            self.root.update_idletasks()
            time.sleep(1)
        self.label.config(text=&quot;끝!&quot;)

if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CountdownApp(root)
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;time.sleep(1) 이 메인 스레드를 붙잡아 두어 화면 갱신이나 다른 입력을 막는다. &lt;br /&gt;카운트다운 중 창을 움직이면 반응하지 않는다. &lt;br /&gt;오래 걸리는 작업을 메인 스레드에서 처리하면 결국 '응답 없음' 상태가 됨을 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnIWSt/dJMcaasCb33/K6413uQpZI8lVs4lSEqrq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnIWSt/dJMcaasCb33/K6413uQpZI8lVs4lSEqrq0/img.png&quot; data-alt=&quot;메인스레드가 작업을 하면서 응답없음이 뜸&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnIWSt/dJMcaasCb33/K6413uQpZI8lVs4lSEqrq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnIWSt%2FdJMcaasCb33%2FK6413uQpZI8lVs4lSEqrq0%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;325&quot; height=&quot;166&quot; data-origin-width=&quot;325&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메인스레드가 작업을 하면서 응답없음이 뜸&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#작업&amp;nbsp;스레드로&amp;nbsp;분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
import threading
import time

class CountdownApp:
    def __init__(self, root):
        self.root = root
        self.label = tk.Label(root, text=&quot;10&quot;, font=(&quot;Arial&quot;, 60))
        self.label.pack(expand=True)
        tk.Button(root, text=&quot;시작&quot;, command=self.start).pack(pady=10)
    def start(self):
        threading.Thread(target=self.count, daemon=True).start()# 작업스레드에 짬떄리기
    def count(self):
        for i in range(10, -1, -1):
            self.label.config(text=str(i))
            time.sleep(1)
        self.label.config(text=&quot;끝!&quot;)
if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CountdownApp(root)
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작&amp;nbsp;버튼을&amp;nbsp;누르면&amp;nbsp;카운트다운&amp;nbsp;작업(count)을&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;자유로운&amp;nbsp;상태가&amp;nbsp;됩니다. &lt;br /&gt;메인&amp;nbsp;스레드가&amp;nbsp;멈추지&amp;nbsp;않아&amp;nbsp;카운트다운&amp;nbsp;중에도&amp;nbsp;창&amp;nbsp;이동,&amp;nbsp;버튼&amp;nbsp;클릭&amp;nbsp;등에&amp;nbsp;정상적으로&amp;nbsp;반응합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#after()로&amp;nbsp;안전하게&amp;nbsp;화면&amp;nbsp;갱신하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
위젯은 메인 스레드에서만 다루어야 함
앞서 작업 스레드로 분리한 코드는 화면이 멈추지 않아 잘 동작하는 것처럼 보입니다.
작업 스레드에서 self.label.config(...) 처럼 위젯을 직접 건드리면, 
당장은 문제가 없어 보여도 상황에 따라 화면이 깨지거나 예기치 않게 프로그램이 멈출 수 있습니다.

그래서 필요한 것이 after()

after(밀리초, 함수)는 &quot;지정한 시간이 흐른 뒤 이 함수를 메인 스레드에서 실행하라&quot;고 예약하는
메서드입니다.
즉 위젯을 바꾸는 일은 메인 스레드에 맡기고, 작업 스레드는 &quot;언제 무엇을 할지&quot;만 예약하는 방
식으로 역할을 다시 나눌 수 있습니다.

after()만으로 카운트다운 구현하기

사실 카운트다운처럼 &quot;1초마다 한 번씩&quot;인 단순한 반복은 스레드 없이 
after() 만으로도 깔끔하게 만들 수 있습니다.
time.sleep 으로 흐름을 멈추는 대신, 다음 숫자를 1초 뒤에 실행하도록 예약하고 함수를 
빠져나오는 것이 핵심입니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#after 예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;import tkinter as tk

class CountdownApp:
    def __init__(self, root):
        self.root = root
        self.label = tk.Label(root, text=&quot;10&quot;, font=(&quot;Arial&quot;, 60))
        self.label.pack(expand=True)
        tk.Button(root, text=&quot;시작&quot;, command=self.start).pack(pady=10)
    def start(self):
        self.count(10)
    def count(self, i):
        if i &amp;lt; 0:
            self.label.config(text=&quot;끝!&quot;)
            return

        self.label.config(text=str(i))
        self.root.after(1000, self.count, i - 1)

if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CountdownApp(root)
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;큐&amp;nbsp;통신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
tkinter 위젯은 메인 스레드에서만 안전하게 조작할 수 있으므로, 
백그라운드 스레드가 위젯을 직접 바꾸면 간헐적으로 충돌합니다.
그래서 큐(queue)를 사이에 둡니다. 백그라운드 스레드는 결과를 큐에 넣기만 하고, 
메인 스레드는 큐에서 결과를 꺼내 화면을 바꿉니다. 역할이 나뉘므로 위젯은 언제나
메인 스레드에서만 조작됩니다

큐(Queue)란

큐는 먼저 넣은 것이 먼저 나오는(FIFO, First In First Out) 자료구조입니다.
넣기(put) &amp;rarr;  [ 1 ][ 2 ][ 3 ]  &amp;rarr; 꺼내기(get)
        뒤            앞
        
리스트로도 비슷하게 흉내 낼 수 있지만, 파이썬의 queue.Queue에는 두 가지 중요한 장점이 있
습니다.

-스레드 안전(thread-safe): 여러 스레드가 동시에 넣고 꺼내도 데이터가 깨지지 않도록 내부
적으로 잠금(lock)을 처리해 줍니다. 그래서 스레드 사이의 데이터 전달 통로로 안성맞춤입니
다.
-대기 기능: 큐가 비어 있을 때 값이 들어올 때까지 기다리게 하거나, 기다리지 않고 즉시 넘어
가게 하는 등 상황에 맞게 선택할 수 있습니다.

기본 사용법

큐 만들기

import queue
q = queue.Queue()          
# 크기 제한 없는 큐
q = queue.Queue(maxsize=5) # 최대 5개까지만 담는 큐

값 꺼내기 ( get , get_nowait )

n = q.get() 
n = q.get_nowait()

get() 은 값이 올 때까지 멈춰 서서 기다립니다. GUI 메인 스레드에서 이걸 쓰면 화면이
얼어붙으므로 주의해야 합니다.
get_nowait() 는 기다리지 않으므로 GUI에 적합합니다. 대신 큐가 비어 있으면 예외가
나므로 try/except로 감싸 처리합니다.

상태 확인
q.empty()  # 비어 있으면 True                
q.qsize()  # 대략적인 크기 (참고용)                
'''
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#스레드와&amp;nbsp;큐&amp;nbsp;활용하기 &lt;br /&gt;#'스레드&amp;nbsp;&amp;rarr;&amp;nbsp;큐&amp;nbsp;&amp;rarr;&amp;nbsp;화면'&amp;nbsp;예제코드 &lt;br /&gt;#백그라운드에서&amp;nbsp;1초마다&amp;nbsp;숫자를&amp;nbsp;세어&amp;nbsp;큐에&amp;nbsp;넣고,&amp;nbsp;메인&amp;nbsp;스레드가&amp;nbsp;이를&amp;nbsp;꺼내&amp;nbsp;라벨에&amp;nbsp;표시합니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import tkinter as tk
import threading
import queue
import time

root = tk.Tk()
label = tk.Label(root, text=&quot;대기 중&quot;, font=(&quot;&quot;, 20))
label.pack(padx=40, pady=40)

q = queue.Queue()

def work():
    for i in range(1, 10):
        time.sleep(1)
        q.put(i)
def poll():
    try:
        while True:
            n = q.get_nowait()
            label.config(text=f&quot;{n} 초&quot;)
    except queue.Empty:
        pass
    root.after(100, poll)

threading.Thread(target=work, daemon=True).start() #daemon은 윈도우가 꺼지면, 이 스레드도 멈춰라
root.after(100, poll)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
카운트 다운 코드 큐 기능 추가

앞서 스레드 강좌를 위한 카운트다운 예제 코드는 백그라운드 스레드에서 GUI를 조작하는 문제
점이 있었습니다.
이를 큐 기능을 추가해 안전한 코드로 아래와 같이 개선했습니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;#큐 기능이 추가된 코드

import tkinter as tk
import threading
import queue
import time
class CountdownApp:
    def __init__(self, root):
        self.root = root
        self.running=False # 카운트다운 중
        self.queue = queue.Queue()
        self.label = tk.Label(root, text=&quot;10&quot;, font=(&quot;Arial&quot;, 60))
        self.label.pack(expand=True)
        tk.Button(root, text=&quot;시작&quot;, command=self.start).pack(pady=10)
        self.poll()
    def start(self):
        if self.running:# 카운트다운 중일땐 스레드를 더이상 만들지 않음
            return
        self.running=True
        threading.Thread(target=self.count, daemon=True).start()

    def count(self):
        for i in range(10, -1, -1):
            self.queue.put(str(i))
            time.sleep(1)
        self.queue.put(&quot;끝!&quot;)
    def poll(self):
        try:
            while True:
                self.label.config(text=self.queue.get_nowait())
        except queue.Empty:
            pass
        self.root.after(100, self.poll)
if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CountdownApp(root)
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카운트다운 모델을 직접 MVP로 구현해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;import tkinter as tk
import threading
import queue
import time
class CountdownModel:
    def __init__(self):
        self.queue = queue.Queue()
    def start(self):
        threading.Thread(target=self.count, daemon=True).start()

    def count(self):
        for i in range(10, -1, -1):
            self.queue.put(str(i))
            time.sleep(1)
        self.queue.put(&quot;끝!&quot;)

class CountdownView(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.label = tk.Label(root, text=&quot;10&quot;, font=(&quot;Arial&quot;, 60))
        self.label.pack(expand=True)
        self.btn=tk.Button(root, text=&quot;시작&quot;)
        self.btn.pack(pady=10)

    def schedule(self, delay, callback):
        self.after(delay, callback)

class CountdownPresenter:
    def __init__(self,model,view):
        self.model = model
        self.view = view
        self.view.btn.config(command=self.start)
    def start(self):
        threading.Thread(target=self.model.count, daemon=True).start()
        self.poll()
    def poll(self):
        try:
            while True:
                self.view.label.config(text=self.model.queue.get_nowait())
        except queue.Empty:
            pass
        self.view.schedule(100,self.poll)
if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CountdownPresenter(CountdownModel(),CountdownView(root))
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본(MVP없이 구현된 코드) 코드를 보았을때, poll 함수에서 root를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def poll(self):
    try:
        while True:
            self.label.config(text=self.queue.get_nowait())
    except queue.Empty:
        pass
    self.root.after(100, self.poll)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-CountdownApp 클래스의 poll 함수&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;그래서 poll함수를 CountdownPresenter클래스에 넣을지, CountdownView 클래스에 넣을지 고민을 했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT한테 물어본 결과&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;Presenter에&lt;span&gt;&amp;nbsp;&lt;/span&gt;root(Tk 객체)를 넘기는 것보다, View가&lt;span&gt;&amp;nbsp;&lt;/span&gt;after()를 관리하도록 두는 쪽이 더 MVP스럽습니다.&lt;/b&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;다만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;poll()&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자체는 View보다 Presenter에 두는 게 더 자연스럽습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 했다. 그래서 view쪽에 schedule이라는 함수를 만들고, (after 실행) 해당 코드를 Presenter클래스의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poll함수에서 호출하는 쪽으로 결정하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;JSON&amp;nbsp;파일&amp;nbsp;이해하기&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;'''
JSON이란 무엇인가?

JSON(JavaScript Object Notation)은 데이터를 텍스트로 표현하는 형식입니다.
읽기 쉽고 구조도 단순해서, 프로그램끼리 데이터를 주고받거나 파일에 데이터를 저장할 때 널리 쓰입니다.

JSON의 생김새

연락처 하나를 JSON으로 표현하면 아래와 같습니다.

{
&quot;name&quot;: &quot;홍길동&quot;,
&quot;company&quot;: &quot;한빛상사&quot;,
&quot;phone&quot;: &quot;010-1234-5678&quot;
}

중괄호 { }로 묶인 이 덩어리를 객체라고 부릅니다. 안에는 &quot;이름&quot;: 값 형태의 쌍이 들어 있습니다.
왼쪽의 &quot;name&quot; 은 항목의 이름(키), 오른쪽은 그 값입니다. 키는 반드시 큰따옴표로 감싸고, 쌍과
쌍 사이는 쉼표로 구분합니다.
연락처는 여러 개가 필요하므로, 객체들을 대괄호 [ ] 로 묶어 배열(목록)로 만듭니다.

파이썬에서 JSON 파일 읽고 쓰기

파이썬에는 JSON을 다루는 json 모듈이 기본으로 들어 있어 따로 설치할 필요가 없습니다. 
핵심 함수는 두 개입니다.

json.load(f) 는 파일에서 JSON을 읽어 파이썬 자료형으로 바꿉니다. 파일 속 배열은 리스트
로, 객체는 딕셔너리로 변환되어 돌아옵니다.

json.dump(데이터, f) 는 반대로 파이썬 자료형을 JSON 텍스트로 바꿔 파일에 씁니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#JSON&amp;nbsp;저장/불러오기&amp;nbsp;코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#앱에서&amp;nbsp;사용할&amp;nbsp;저장/불러오기&amp;nbsp;코드를&amp;nbsp;보면&amp;nbsp;이&amp;nbsp;두&amp;nbsp;함수가&amp;nbsp;어떻게&amp;nbsp;쓰이는지&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있습니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import os, json

FILE = &quot;contacts.json&quot;

def load():
    if os.path.exists(FILE):
        with open(FILE, encoding=&quot;utf-8&quot;) as f:
            return json.load(f)
    return []
    
def save():
    with open(FILE, &quot;w&quot;, encoding=&quot;utf-8&quot;) as f:
        json.dump(contacts, f, ensure_ascii=False, indent=2)
        
contacts = load()
save()
'''
알아두기

with open(...) 구문과 인코딩 지정

with open(...) 구문을 쓰면 작업이 끝났을 때 파일이 자동으로 닫혀 안전합니다.
encoding=&quot;utf-8&quot; 은 한글이 깨지지 않도록 문자 인코딩을 지정하는 부분입니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행시 디렉토리에 contacts.json이 생성됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#입력폼&amp;nbsp;생성하고&amp;nbsp;연락처&amp;nbsp;추가하기 &lt;br /&gt;#목표 &lt;br /&gt;#contacts.json&amp;nbsp;파일에&amp;nbsp;'이름','회사','연락처'를&amp;nbsp;추가할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;최소한의&amp;nbsp;GUI&amp;nbsp;폼을&amp;nbsp;작성합니다. &lt;br /&gt;#연락처&amp;nbsp;v0.1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
import os, json

FILE = &quot;contacts.json&quot;

def load():
    if os.path.exists(FILE):
        with open(FILE, encoding=&quot;utf-8&quot;) as f:
            return json.load(f)
    return []

def save():
    with open(FILE, &quot;w&quot;, encoding=&quot;utf-8&quot;) as f:
        json.dump(contacts, f, ensure_ascii=False, indent=2)

def add():
    contacts.append({
        &quot;name&quot;: e_name.get(),
        &quot;company&quot;: e_company.get(),
        &quot;phone&quot;: e_phone.get(),
    })
    save()

contacts = load()

root = tk.Tk()
root.title(&quot;연락처 v0.1&quot;)

e_name = tk.Entry(root)
tk.Label(root, text=&quot;이름&quot;).grid(row=0, column=0)
tk.Label(root, text=&quot;회사&quot;).grid(row=0, column=1)
tk.Label(root, text=&quot;전화&quot;).grid(row=0, column=2)
e_company = tk.Entry(root)
e_phone = tk.Entry(root)
e_name.grid(row=1, column=0)
e_company.grid(row=1, column=1)
e_phone.grid(row=1, column=2)
tk.Button(root, text=&quot;추가&quot;, command=add).grid(row=3, column=1)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQRcyi/dJMcaf8pI3M/zNWtOWwEiuPvcKZsn1P2Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQRcyi/dJMcaf8pI3M/zNWtOWwEiuPvcKZsn1P2Jk/img.png&quot; data-alt=&quot;실행결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQRcyi/dJMcaf8pI3M/zNWtOWwEiuPvcKZsn1P2Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQRcyi%2FdJMcaf8pI3M%2FzNWtOWwEiuPvcKZsn1P2Jk%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;434&quot; height=&quot;98&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCuQxU/dJMcah6npfg/SAXRokoDjcK9LWpwKCKP2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCuQxU/dJMcah6npfg/SAXRokoDjcK9LWpwKCKP2k/img.png&quot; data-alt=&quot;저장된 json파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCuQxU/dJMcah6npfg/SAXRokoDjcK9LWpwKCKP2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCuQxU%2FdJMcah6npfg%2FSAXRokoDjcK9LWpwKCKP2k%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;745&quot; height=&quot;504&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저장된 json파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#Treeview&amp;nbsp;위젯으로&amp;nbsp;연락처&amp;nbsp;보기 &lt;br /&gt;&lt;br /&gt;#목표 &lt;br /&gt;#ttk.Treeview&amp;nbsp;위젯으로&amp;nbsp;'contacts.json'&amp;nbsp;파일&amp;nbsp;내&amp;nbsp;연락처&amp;nbsp;정보를&amp;nbsp;엑셀&amp;nbsp;시트처럼&amp;nbsp;출력합니다. &lt;br /&gt;#연락처&amp;nbsp;삭제&amp;nbsp;기능을&amp;nbsp;추가합니다. &lt;br /&gt;#연락처&amp;nbsp;v0.2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk, messagebox
import os, json

FILE = &quot;contacts.json&quot;

def load():
    if os.path.exists(FILE):
        with open(FILE, encoding=&quot;utf-8&quot;) as f:
            return json.load(f)
    return []

def save():
    with open(FILE, &quot;w&quot;, encoding=&quot;utf-8&quot;) as f:
        json.dump(contacts, f, ensure_ascii=False, indent=2)

def refresh():
    tree.delete(*tree.get_children())
    for c in contacts:
        tree.insert(&quot;&quot;, &quot;end&quot;, values=(c[&quot;name&quot;], c[&quot;company&quot;], c[&quot;phone&quot;]))

def add():
    name, company, phone = e_name.get(), e_company.get(), e_phone.get()
    if not name:
        messagebox.showwarning(&quot;확인&quot;, &quot;이름을 입력하세요.&quot;)
        return
    contacts.append({&quot;name&quot;: name, &quot;company&quot;: company, &quot;phone&quot;: phone})
    save()
    refresh()
    for e in (e_name, e_company, e_phone):
        e.delete(0, &quot;end&quot;)

def delete():
    sel = tree.selection()
    if not sel:
        return
    idx = tree.index(sel[0])
    contacts.pop(idx)
    save()
    refresh()

contacts = load()

root = tk.Tk()
root.title(&quot;연락처 v0.2&quot;)

input_frame = tk.LabelFrame(root, text=&quot;입력&quot;, padx=10, pady=10)
input_frame.pack(padx=10, pady=10, fill=&quot;x&quot;)

tk.Label(input_frame, text=&quot;이름&quot;).grid(row=0, column=0)
tk.Label(input_frame, text=&quot;회사&quot;).grid(row=0, column=1)
tk.Label(input_frame, text=&quot;전화&quot;).grid(row=0, column=2)

e_name = tk.Entry(input_frame)
e_company = tk.Entry(input_frame)
e_phone = tk.Entry(input_frame)
e_name.grid(row=1, column=0, padx=2)
e_company.grid(row=1, column=1, padx=2)
e_phone.grid(row=1, column=2, padx=2)

tk.Button(input_frame, text=&quot;추가&quot;, command=add).grid(row=1, column=3, padx=5)
tk.Button(input_frame, text=&quot;삭제&quot;, command=delete).grid(row=1, column=4)

list_frame = tk.LabelFrame(root, text=&quot;목록&quot;, padx=10, pady=10)
list_frame.pack(padx=10, pady=(0, 10), fill=&quot;both&quot;)

tree = ttk.Treeview(list_frame, columns=(&quot;name&quot;, &quot;company&quot;, &quot;phone&quot;), show=&quot;headings&quot;, height=8)
tree.heading(&quot;name&quot;, text=&quot;이름&quot;)
tree.heading(&quot;company&quot;, text=&quot;회사&quot;)
tree.heading(&quot;phone&quot;, text=&quot;전화&quot;)
tree.pack()

refresh()
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vUa7z/dJMcadW8nSj/B1FVhKDCNnkFnt7ckTf5e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vUa7z/dJMcadW8nSj/B1FVhKDCNnkFnt7ckTf5e1/img.png&quot; data-alt=&quot;실행결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vUa7z/dJMcadW8nSj/B1FVhKDCNnkFnt7ckTf5e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvUa7z%2FdJMcadW8nSj%2FB1FVhKDCNnkFnt7ckTf5e1%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;648&quot; height=&quot;373&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#연락처 v0.3&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;위 코드를 MVP형식으로 재구성.&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;#model&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;'''
모델

설명

데이터를 담당하는 모델부터 만듭니다.
절차적 코드에서 흩어져 있던 load, save, 그리고 연락처 리스트를 다루던 부분이 하나의 클래스
로 모입니다.
모델은 데이터만 다루고 화면이나 입력은 전혀 알지 못한다는 점이 핵심입니다
'''
import os, json
class ContactModel:
    def __init__(self, file_path=&quot;contacts.json&quot;):
        self.file_path = file_path
        self.contacts = self._load()
    def _load(self):
        if os.path.exists(self.file_path):
            with open(self.file_path, encoding=&quot;utf-8&quot;) as f:
                return json.load(f)
        return []
    def _save(self):
        with open(self.file_path, &quot;w&quot;, encoding=&quot;utf-8&quot;) as f:
            json.dump(self.contacts, f, ensure_ascii=False, indent=2)

    def get_all(self):
        return self.contacts

    def add(self, name, company, phone):
        self.contacts.append({&quot;name&quot;: name, &quot;company&quot;: company, &quot;phone&quot;: phone})
        self._save()
    def delete(self, idx):
        self.contacts.pop(idx)
        self._save()

'''
이름 앞에 밑줄이 붙은 _load 와 get_all 는 클래스 내부에서만 쓰는 메서드라는 신호입니다.
바깥에서는 , add ,_save, delete 처럼 밑줄 없는 메서드만 호출하고, 파일을 실제로 읽고
쓰는 세부 동작은 클래스 안에 감춰 둔 것입니다.
add 나 delete 가 끝날 때마다 스스로 _save 를 호출하므로,호출하는 쪽은 저장을 신경 쓸
필요가 없습니다
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#view&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;'''
뷰
설명

화면을 담당하는 뷰입니다. 입력창&amp;middot;버튼&amp;middot;목록을 그리고 입력값을 넘겨주거나 목록을 다시 그립니다.
저장이나 검증은 하지 않고, 버튼을 눌렀을 때의 처리는 프리젠터에게 맡깁니다
'''

import tkinter as tk
from tkinter import ttk, messagebox

class ContactView:
    def __init__(self, root):
        self.root = root
        self.presenter = None
        self._setup_ui()

    def set_presenter(self, presenter):
        self.presenter = presenter

    def _setup_ui(self):
        input_frame = tk.LabelFrame(self.root, text=&quot;입력&quot;, padx=10, pady=10)
        input_frame.pack(padx=10, pady=10, fill=&quot;x&quot;)

        tk.Label(input_frame, text=&quot;이름&quot;).grid(row=0, column=0)
        tk.Label(input_frame, text=&quot;회사&quot;).grid(row=0, column=1)
        tk.Label(input_frame, text=&quot;전화&quot;).grid(row=0, column=2)

        self.e_name = tk.Entry(input_frame)
        self.e_company = tk.Entry(input_frame)
        self.e_phone = tk.Entry(input_frame)

        self.e_name.grid(row=1, column=0, padx=2)
        self.e_company.grid(row=1, column=1, padx=2)
        self.e_phone.grid(row=1, column=2, padx=2)

        tk.Button(input_frame, text=&quot;추가&quot;,
                  command=lambda: self.presenter.add()).grid(row=1, column=3, padx=5)
        tk.Button(input_frame, text=&quot;삭제&quot;,
                  command=lambda: self.presenter.delete()).grid(row=1, column=4)
        list_frame = tk.LabelFrame(self.root, text=&quot;목록&quot;, padx=10, pady=10)
        list_frame.pack(padx=10, pady=(0, 10), fill=&quot;both&quot;, expand=True)

        self.tree = ttk.Treeview(list_frame, columns=(&quot;name&quot;, &quot;company&quot;, &quot;phone&quot;),
                                 show=&quot;headings&quot;, height=8)
        self.tree.heading(&quot;name&quot;, text=&quot;이름&quot;)
        self.tree.heading(&quot;company&quot;, text=&quot;회사&quot;)
        self.tree.heading(&quot;phone&quot;, text=&quot;전화&quot;)
        self.tree.pack(fill=&quot;both&quot;, expand=True)

    def get_inputs(self):
        return self.e_name.get(), self.e_company.get(), self.e_phone.get()

    def clear_inputs(self):
        for e in (self.e_name, self.e_company, self.e_phone):
            e.delete(0, &quot;end&quot;)

    def get_selected_index(self):
        sel = self.tree.selection()
        if not sel:
            return None
        return self.tree.index(sel[0])

    def render(self, contacts):
        self.tree.delete(*self.tree.get_children())

        for c in contacts:
            self.tree.insert(&quot;&quot;, &quot;end&quot;, values=(c[&quot;name&quot;], c[&quot;company&quot;], c[&quot;phone&quot;]))

    def show_warning(self, title, message):
        messagebox.showwarning(title, message)

'''
버튼의 command 가 lambda: self.presenter.add() 로 되어 있는 데 주목합니다.
이전에는 버튼이 직접 add 함수를 불렀지만, 이제는 프리젠터에게 &quot;추가해 달라&quot;고 요청만 합
니다.get_inputs , clear_inputs , render 처럼 뷰가 제공하는 메서드들은 모두 화면을 읽거나 다
시 그리는 창구 역할을 하며, 프리젠터는 이 창구를 통해서만 화면과 소통합니다.
view.py 코드 하단에 아래 코드를 추가하면 UI만 확인할 수 있습니다.

if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    view = ContactView(root)
    root.mainloop()
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#presenter&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;'''
프리젠터

설명

서로 모르는 모델과 뷰 사이를 오가며 흐름을 조율하는 것이 프리젠터입니다
절차적 코드에서 add , delete , refresh 함수가 하던 &quot;판단과 조율&quot;의 로직이 이곳으로 모입
니다
'''

class ContactPresenter:
    def __init__(self, view, model):
        self.view = view
        self.model = model
        self.view.set_presenter(self)
        self.refresh()
    def refresh(self):
        self.view.render(self.model.get_all())
    def add(self):
        name, company, phone = self.view.get_inputs()
        if not name:
            self.view.show_warning(&quot;확인&quot;, &quot;이름을 입력하세요.&quot;)
            return
        self.model.add(name, company, phone)
        self.refresh()
        self.view.clear_inputs()
    def delete(self):
        idx = self.view.get_selected_index()
        if idx is None:
            return
        self.model.delete(idx)
        self.refresh()

'''
add의 흐름을 따라가 보면 세 부분이 어떻게 협력하는지 잘 드러납니다.
프리젠터는 먼저 뷰에서 입력값을 받아 오고(get_inputs ), 이름이 비었는지 검증한 뒤, 문제가
없으면 모델에 저장을 지시하고(model.add ), 마지막으로 화면을 다시 그리고 입력창을 비웁니
다.
이름 검증 같은 &quot;규칙&quot;은 뷰도 모델도 아닌 프리젠터가 쥐고 있습니다.
생성자에서 set_presenter 로 자신을 뷰에 등록하고 곧바로 
refresh 를 호출해, 앱이 켜지자
마자 저장돼 있던 연락처가 화면에 나타나게 합니다.

mvp는 이해만 하고 넘어가면 안됨.
스스로 설계할 줄 알아야한다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#main&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;#세 파일을 모두 만들었으니, 이제 이들을 실제로 조립해 실행하는 진입점만 남았습니다.
import tkinter as tk

from model import ContactModel
from view import ContactView
from presenter import ContactPresenter

def main():
    root = tk.Tk()
    root.title(&quot;연락처 v0.3&quot;)

    view = ContactView(root)
    model = ContactModel()
    ContactPresenter(view, model)

    root.mainloop()

if __name__ == &quot;__main__&quot;:
    main()

'''
main 함수가 하는 일은 세 조각을 만들어 연결하는 것뿐입니다.
뷰와 모델을 각각 생성한 뒤, 이 둘을 프리젠터에 넘겨 주면 나머지 연결은 프리젠터가 알아서 처
리합니다.
조립 과정이 단순하다는 것 자체가 MVP 패턴이 역할을 잘 나눠 놓았다는 증거입니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;&lt;i&gt;&lt;b&gt;네트워크&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
HTTP(HyperText Transfer Protocol)/ HTTPS(HyperText Transfer Protocol Secret)
인터넷에서 우리가 매일 보는 웹 페이지(HTML),이미지, 텍스트 등의 데이터를 주고받기 위해
사용하는 가장 대표적인 웹 통신 규약

ICMP(Internet Control Message Protocol): ping test
데이터를 주고받기 위한 목적보다는, 네트워크의 상태를 진단하고
통신 오류를 알려주기 위해 만들어진 프로토콜

TCP(Transmission Control Protocol)
인터넷상에서 데이터를 빠짐없이, 순서대로, 정확하게 주고 받기 위해
상호 간에 신뢰할 수 있는 연결을 맺는 프로토콜

UDP(User Datagram Protocol)
인터넷에서 데이터를 주고 받을 때 정확성이나 순서 보다는 '속도'를 최우선으로
하는 통신 규약

FTP()
파일을 주고받는 프로토콜

==============================================
모듈
socket : 네트워크의 가장 낮은 수준에서 TCP 연결을 직접 맺어 포트 상태를 확인할 때 사용
subprocess : 파이썬 코드 안에서 운영체제의 셸(CMD, 터미널) 명령어를 실행하고 결과를 받아온다.
urllib.request :  HTTP/HTTPS/ 웹 페이지를 요청하고 서버의 응답(상태 코드 등)을 파싱할 때 사용
==============================================
cheker.py(위의 각 네트워크 점검에 필요한 함수들을 보유하고 있는 기본 클래스)
Checker 클래스 : run()를 정의만..

HttpChecker 클래스 : 웹 사이트 점검
    서버에서 반환한 reaponse코드가 200인지 확인 200이면 True
    urllib.request.urlopen()를 이용하여 설정된 URL로 접속을 시도.
    
IcmpChecker 클래스 : ping 점검
 returncode가 0인지를 확인
 platform.system()를 이용하여 Window, Mac, Linux인지를 판별 ping을 1회 보내고 경과를 확인
 subprocess.run()를 이용하여 파이썬 내부 터미널 명령어를 직접 실행. 그 결과를 캡처.
 내부 터미널 명령어가 오류없이 정상적으로 종료(returncode==0)이면 True
 
TcpChecker 클래스 : port 개방 점검
 지정한 port가 열려 있으면 True
 socket.create_connection()를 이용하여 IP의 해당 port로 직접 TCP연결을 시도
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
&quot;&quot;&quot;
교재 예제 파일: 네트워크 점검 스레드 적용
&quot;&quot;&quot;

import tkinter as tk
from tkinter import ttk, messagebox
import threading
import queue
from checker import HttpChecker, IcmpChecker, TcpChecker

TYPES = {&quot;HTTP/HTTPS&quot;: HttpChecker, &quot;ICMP&quot;: IcmpChecker, &quot;TCP&quot;: TcpChecker}


class App:
    def __init__(self, root):
        self.root = root
        root.geometry(&quot;700x450&quot;)

        self.checkers = {}
        self.result_queue = queue.Queue()

        top = ttk.Frame(root, padding=10)
        top.pack(fill=&quot;x&quot;)
        self.type_cb = ttk.Combobox(top, values=list(TYPES), state=&quot;readonly&quot;, width=12)
        self.type_cb.current(0)
        self.type_cb.pack(side=&quot;left&quot;)
        self.target_entry = ttk.Entry(top, width=30)
        self.target_entry.pack(side=&quot;left&quot;, padx=5)
        ttk.Label(top, text=&quot;포트:&quot;).pack(side=&quot;left&quot;)
        self.port_entry = ttk.Entry(top, width=8)
        self.port_entry.pack(side=&quot;left&quot;, padx=(2, 5))
        ttk.Button(top, text=&quot;추가&quot;, command=self.add).pack(side=&quot;left&quot;)

        cols = (&quot;type&quot;, &quot;target&quot;, &quot;result&quot;, &quot;detail&quot;)
        self.tree = ttk.Treeview(root, columns=cols, show=&quot;headings&quot;)
        for c, t in zip(cols, (&quot;유형&quot;, &quot;대상&quot;, &quot;결과&quot;, &quot;상세&quot;)):
            self.tree.heading(c, text=t)
        self.tree.tag_configure(&quot;ok&quot;, foreground=&quot;green&quot;)
        self.tree.tag_configure(&quot;fail&quot;, foreground=&quot;red&quot;)
        self.tree.pack(fill=&quot;both&quot;, expand=True, padx=10)

        bottom = ttk.Frame(root, padding=10)
        bottom.pack(fill=&quot;x&quot;)
        self.check_btn = ttk.Button(bottom, text=&quot;전체 점검&quot;, command=self.check_all)
        self.check_btn.pack(side=&quot;left&quot;)
        ttk.Button(bottom, text=&quot;전체 삭제&quot;, command=self.clear).pack(side=&quot;left&quot;, padx=5)

        self.root.after(100, self.process_queue)

    def add(self):
        ctype = self.type_cb.get()
        cls = TYPES[ctype]
        target = self.target_entry.get().strip()
        port = self.port_entry.get().strip()
        if not target:
            messagebox.showwarning(&quot;입력 오류&quot;, &quot;대상을 입력하세요.&quot;)
            return

        if cls is TcpChecker:
            if not port.isdigit():
                messagebox.showwarning(&quot;입력 오류&quot;, &quot;TCP는 숫자 포트가 필요합니다.&quot;)
                return
            checker = cls(target, port)
            shown = f&quot;{target}:{port}&quot;
        else:
            checker = cls(target)
            shown = target

        iid = self.tree.insert(&quot;&quot;, tk.END, values=(ctype, shown, &quot;-&quot;, &quot;&quot;))
        self.checkers[iid] = checker
        self.target_entry.delete(0, tk.END)

    def check_all(self):
        if not self.checkers:
            return

        self.check_btn.config(state=&quot;disabled&quot;)
        for iid in self.checkers:
            self.tree.set(iid, &quot;result&quot;, &quot;점검 중&amp;hellip;&quot;)
            self.tree.set(iid, &quot;detail&quot;, &quot;&quot;)
            self.tree.item(iid, tags=())

        targets = list(self.checkers.items())
        thread = threading.Thread(target=self.run_checks, args=(targets,), daemon=True)
        thread.start()

    def run_checks(self, targets):
        for iid, checker in targets:
            try:
                ok, detail = checker.run()
                self.result_queue.put((iid, &quot;정상&quot; if ok else &quot;실패&quot;, detail,
                                       &quot;ok&quot; if ok else &quot;fail&quot;))
            except Exception as e:
                self.result_queue.put((iid, &quot;오류&quot;, str(e), &quot;fail&quot;))
        self.result_queue.put((&quot;__done__&quot;, None, None, None))

    def process_queue(self):
        try:
            while True:
                iid, result, detail, tag = self.result_queue.get_nowait()
                if iid == &quot;__done__&quot;:
                    self.check_btn.config(state=&quot;normal&quot;)
                    continue

                if self.tree.exists(iid):
                    self.tree.set(iid, &quot;result&quot;, result)
                    self.tree.set(iid, &quot;detail&quot;, detail)
                    self.tree.item(iid, tags=(tag,))
        except queue.Empty:
            pass
        finally:
            self.root.after(100, self.process_queue)

    def clear(self):
        for iid in self.tree.get_children():
            self.tree.delete(iid)
        self.checkers.clear()


if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    App(root)
    root.mainloop()

# ==================================================
# 참고 예제 코드 (Reference Code)
# ==================================================

# [코드 조각]
# self.result_queue = queue.Queue()

# [코드 조각]
# self.root.after(100, self.process_queue)

# [코드 조각]
# def check_all(self):
#     if not self.checkers:
#         return
# 
#     self.check_btn.config(state=&quot;disabled&quot;)
#     for iid in self.checkers:
#         self.tree.set(iid, &quot;result&quot;, &quot;점검 중&amp;hellip;&quot;)
#         self.tree.set(iid, &quot;detail&quot;, &quot;&quot;)
#         self.tree.item(iid, tags=())
# 
#     targets = list(self.checkers.items())
#     thread = threading.Thread(target=self.run_checks, args=(targets,), daemon=True)
#     thread.start()

# [코드 조각]
# def run_checks(self, targets):
#     for iid, checker in targets:
#         try:
#             ok, detail = checker.run()
#             self.result_queue.put((iid, &quot;정상&quot; if ok else &quot;실패&quot;, detail,
#                                    &quot;ok&quot; if ok else &quot;fail&quot;))
#         except Exception as e:
#             self.result_queue.put((iid, &quot;오류&quot;, str(e), &quot;fail&quot;))
#     self.result_queue.put((&quot;__done__&quot;, None, None, None))

# [코드 조각]
# def process_queue(self):
#     try:
#         while True:
#             iid, result, detail, tag = self.result_queue.get_nowait()
#             if iid == &quot;__done__&quot;:
#                 self.check_btn.config(state=&quot;normal&quot;)
#                 continue
#             if self.tree.exists(iid):
#                 self.tree.set(iid, &quot;result&quot;, result)
#                 self.tree.set(iid, &quot;detail&quot;, detail)
#                 self.tree.item(iid, tags=(tag,))
#     except queue.Empty:
#         pass
#     finally:
#         self.root.after(100, self.process_queue)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 설명은 pdf 04에..&lt;/p&gt;</description>
      <category>데이터 분석</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/11</guid>
      <comments>https://bb0ng2.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 22 Jul 2026 20:56:42 +0900</pubDate>
    </item>
    <item>
      <title>0721 아키텍처/데이터파이프라인 +GUI</title>
      <link>https://bb0ng2.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;아키텍처 (Architecture)&lt;/b&gt;&lt;/i&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;IT&amp;nbsp;분야에서&amp;nbsp;아키텍처(Architecture)란 &lt;br /&gt;소프트웨어나&amp;nbsp;시스템을&amp;nbsp;구성하는&amp;nbsp;핵심&amp;nbsp;요소들과 &lt;br /&gt;그&amp;nbsp;요소들이&amp;nbsp;상호작용하는&amp;nbsp;방식 &lt;br /&gt;그리고&amp;nbsp;전체&amp;nbsp;시스템을&amp;nbsp;설계하고&amp;nbsp;발전시키는&amp;nbsp;기반&amp;nbsp;규칙을&amp;nbsp;의미 &lt;br /&gt;&lt;br /&gt;쉽게&amp;nbsp;말해&amp;nbsp;건물을&amp;nbsp;지을&amp;nbsp;때&amp;nbsp;뼈대와&amp;nbsp;배관,&amp;nbsp;전기&amp;nbsp;회선의&amp;nbsp;위치를&amp;nbsp;잡는&amp;nbsp;'건축&amp;nbsp;설계도'와&amp;nbsp;같다. &lt;br /&gt;인테리어(UI/UX)를&amp;nbsp;꾸미기&amp;nbsp;전에&amp;nbsp;건물이&amp;nbsp;무너지지&amp;nbsp;않고&amp;nbsp;효율적으로&amp;nbsp;기능하도록&amp;nbsp;튼튼한&amp;nbsp;구조를&amp;nbsp;잡는&amp;nbsp;작업 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;아키텍처의&amp;nbsp;핵심&amp;nbsp;구성&amp;nbsp;요소&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;아키텍처는&amp;nbsp;보통&amp;nbsp;다음&amp;nbsp;세&amp;nbsp;가지를&amp;nbsp;정의. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;컴포넌트(Components):&lt;/i&gt; &lt;br /&gt;시스템을&amp;nbsp;이루는&amp;nbsp;조각들입니다. &lt;br /&gt;프론트엔드(화면),&amp;nbsp;백엔드&amp;nbsp;서버,&amp;nbsp;데이터베이스,&amp;nbsp;외부&amp;nbsp;API&amp;nbsp;등이&amp;nbsp;여기에&amp;nbsp;해당합니다. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;관계와&amp;nbsp;상호작용(Interactions):&lt;/i&gt; &lt;br /&gt;컴포넌트들이&amp;nbsp;어떻게&amp;nbsp;데이터를&amp;nbsp;주고받는지&amp;nbsp;정의.(우리는&amp;nbsp;데이터&amp;nbsp;파이프라인) &lt;br /&gt;(예:&amp;nbsp;&quot;사용자가&amp;nbsp;로그인하면,&amp;nbsp;서버는&amp;nbsp;데이터베이스에서&amp;nbsp;정보를&amp;nbsp;확인하고&amp;nbsp;화면에&amp;nbsp;결과를&amp;nbsp;반환한다.&quot;) &lt;br /&gt;&lt;br /&gt;&lt;i&gt;제약&amp;nbsp;조건과&amp;nbsp;규칙(Rules&amp;nbsp;&amp;amp;&amp;nbsp;Constraints):&lt;/i&gt; &lt;br /&gt;보안&amp;nbsp;기준,&amp;nbsp;데이터&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;원칙. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;주요&amp;nbsp;아키텍처의&amp;nbsp;종류&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;관점에&amp;nbsp;따라&amp;nbsp;여러&amp;nbsp;가지로&amp;nbsp;나뉘지만,&amp;nbsp;대표적으로&amp;nbsp;두&amp;nbsp;가지. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;시스템&amp;nbsp;아키텍처&amp;nbsp;(인프라&amp;nbsp;관점):&lt;/i&gt; &lt;br /&gt;하드웨어,&amp;nbsp;네트워크,&amp;nbsp;클라우드&amp;nbsp;서버,&amp;nbsp;데이터베이스&amp;nbsp;등 &lt;br /&gt;물리적/가상적&amp;nbsp;자원들이&amp;nbsp;어떻게&amp;nbsp;배치되고&amp;nbsp;연결되는지?. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;소프트웨어&amp;nbsp;아키텍처&amp;nbsp;(코드&amp;nbsp;관점):&lt;/i&gt; &lt;br /&gt;개발자들이&amp;nbsp;작성하는&amp;nbsp;애플리케이션&amp;nbsp;코드를 &lt;br /&gt;어떤&amp;nbsp;패턴(예:&amp;nbsp;MVC&amp;nbsp;패턴)과&amp;nbsp;구조로&amp;nbsp;분리하여&amp;nbsp;짤&amp;nbsp;것인지?. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;가장&amp;nbsp;대표적인&amp;nbsp;아키텍처&amp;nbsp;패턴&amp;nbsp;비교&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;모놀리식(Monolithic)&lt;/b&gt;은&amp;nbsp;모든&amp;nbsp;시설이&amp;nbsp;한&amp;nbsp;층에&amp;nbsp;모여&amp;nbsp;있는&amp;nbsp;대형&amp;nbsp;마트이고, &lt;br /&gt;&lt;b&gt;마이크로서비스(MSA)&lt;/b&gt;는&amp;nbsp;개별&amp;nbsp;매장들이&amp;nbsp;골목에&amp;nbsp;모여&amp;nbsp;상권을&amp;nbsp;이룬&amp;nbsp;전통&amp;nbsp;시장! &lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;모놀리식(Monolithic)&amp;nbsp;아키텍처&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;소프트웨어의&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;작성되고, &lt;br /&gt;하나의&amp;nbsp;통짜&amp;nbsp;애플리케이션으로&amp;nbsp;배포되는&amp;nbsp;전통적인&amp;nbsp;구조. &lt;br /&gt;&lt;br /&gt;장점: &lt;br /&gt;초기&amp;nbsp;개발&amp;nbsp;속도가&amp;nbsp;매우&amp;nbsp;빠르다. &lt;br /&gt;코드가&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;단순. &lt;br /&gt;&lt;br /&gt;단점: &lt;br /&gt;서비스가&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;배포해야&amp;nbsp;한다. &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;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;마이크로서비스&amp;nbsp;아키텍처&amp;nbsp;(MSA)&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;애플리케이션을&amp;nbsp;핵심&amp;nbsp;비즈니스&amp;nbsp;기능(예:&amp;nbsp;회원&amp;nbsp;서비스,&amp;nbsp;주문&amp;nbsp;서비스,&amp;nbsp;결제&amp;nbsp;서비스)&amp;nbsp;단위로&amp;nbsp;잘게&amp;nbsp;쪼개고, &lt;br /&gt;각각&amp;nbsp;독립적으로&amp;nbsp;작동하는&amp;nbsp;서비스들의&amp;nbsp;조합으로&amp;nbsp;구축하는&amp;nbsp;방식. &lt;br /&gt;서비스들은&amp;nbsp;주로&amp;nbsp;API(네트워크)를&amp;nbsp;통해&amp;nbsp;서로&amp;nbsp;통신. &lt;br /&gt;&lt;br /&gt;장점: &lt;br /&gt;부분적인&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;업데이트할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;트래픽이&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;있다. &lt;br /&gt;하나의&amp;nbsp;서비스가&amp;nbsp;다운되어도&amp;nbsp;전체&amp;nbsp;시스템은&amp;nbsp;계속&amp;nbsp;작동(장애&amp;nbsp;격리). &lt;br /&gt;&lt;br /&gt;단점: &lt;br /&gt;네트워크&amp;nbsp;통신이&amp;nbsp;필수적이므로 &lt;br /&gt;전체적인&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;하기가&amp;nbsp;어렵다. &lt;br /&gt;초기&amp;nbsp;아키텍처&amp;nbsp;설계와&amp;nbsp;인프라&amp;nbsp;구축&amp;nbsp;비용이&amp;nbsp;높다. &lt;br /&gt;&lt;br /&gt;========================================================= &lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;데이터 파이프라인(Data Pipeline)&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;데이터&amp;nbsp;파이프라인은 &lt;br /&gt;다양한&amp;nbsp;소스(출처)에서&amp;nbsp;발생한&amp;nbsp;원시&amp;nbsp;데이터(Raw&amp;nbsp;Data)를&amp;nbsp;수집하여, &lt;br /&gt;분석이나&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;이동시키는 &lt;br /&gt;'자동화된 데이터 흐름 체계'를 말한다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;데이터&amp;nbsp;웨어하우스&amp;nbsp;(Data&amp;nbsp;Warehouse)&amp;nbsp;:&lt;/b&gt; &lt;br /&gt;개념:&amp;nbsp;분석을&amp;nbsp;위해&amp;nbsp;사전에&amp;nbsp;정의된&amp;nbsp;규칙(스키마)에&amp;nbsp;맞게&amp;nbsp;잘&amp;nbsp;정제되고&amp;nbsp;구조화된&amp;nbsp;데이터만&amp;nbsp;모아두는&amp;nbsp;중앙&amp;nbsp;저장소. &lt;br /&gt;데이터&amp;nbsp;형태:&amp;nbsp;엑셀이나&amp;nbsp;관계형&amp;nbsp;데이터베이스(RDB)처럼&amp;nbsp;행(Row)과&amp;nbsp;열(Column)이&amp;nbsp;명확한&amp;nbsp;구조화된&amp;nbsp;데이터(Structured&amp;nbsp;Data)만&amp;nbsp;저장 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;데이터&amp;nbsp;레이크&amp;nbsp;(Data&amp;nbsp;Lake)&lt;/b&gt; &lt;br /&gt;비유:&amp;nbsp;비,&amp;nbsp;강물,&amp;nbsp;지하수&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;형태의&amp;nbsp;물이&amp;nbsp;자연&amp;nbsp;그대로&amp;nbsp;흘러들어와&amp;nbsp;고이는&amp;nbsp;'거대한&amp;nbsp;자연&amp;nbsp;호수' &lt;br /&gt;개념:&amp;nbsp;데이터의&amp;nbsp;용도나&amp;nbsp;형태를&amp;nbsp;미리&amp;nbsp;정하지&amp;nbsp;않고,&amp;nbsp;생성된&amp;nbsp;원시&amp;nbsp;데이터(Raw&amp;nbsp;Data)를&amp;nbsp;가공하지&amp;nbsp;않은&amp;nbsp;상태&amp;nbsp;그대로&amp;nbsp;무한정&amp;nbsp;쏟아붓는&amp;nbsp;방대한&amp;nbsp;저장소 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;데이터&amp;nbsp;레이크하우스:&lt;/b&gt; &lt;br /&gt;데이터&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;성능(SQL&amp;nbsp;엔진)을&amp;nbsp;얹은&amp;nbsp;통합&amp;nbsp;플랫폼 &lt;br /&gt;&lt;br /&gt;쉽게&amp;nbsp;비유하자면&amp;nbsp;'정수장의&amp;nbsp;상수도&amp;nbsp;시스템'과&amp;nbsp;같다. &lt;br /&gt;강물이나&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;br /&gt;최종적으로&amp;nbsp;각&amp;nbsp;가정의&amp;nbsp;수도꼭지(데이터&amp;nbsp;분석가,&amp;nbsp;AI&amp;nbsp;모델&amp;nbsp;등&amp;nbsp;목적지)로&amp;nbsp;깨끗한&amp;nbsp;물을&amp;nbsp;배달하는전체&amp;nbsp;인프라가&amp;nbsp;바로&amp;nbsp;데이터&amp;nbsp;파이프라인. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;핵심&amp;nbsp;구성&amp;nbsp;요소&amp;nbsp;(ETL&amp;nbsp;과정)&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;데이터&amp;nbsp;파이프라인은&amp;nbsp;일반적으로&amp;nbsp;ETL이라는&amp;nbsp;세&amp;nbsp;가지&amp;nbsp;주요&amp;nbsp;단계를&amp;nbsp;거쳐&amp;nbsp;구축.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;(최근에는&amp;nbsp;클라우드의&amp;nbsp;발달로&amp;nbsp;순서가&amp;nbsp;바뀐&amp;nbsp;ELT&amp;nbsp;방식도&amp;nbsp;많이&amp;nbsp;쓰입니다.) &lt;br /&gt;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;&lt;i&gt;추출&amp;nbsp;(Extract):&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;웹사이트&amp;nbsp;접속&amp;nbsp;로그,&amp;nbsp;결제&amp;nbsp;시스템의&amp;nbsp;트랜잭션,&amp;nbsp;소셜&amp;nbsp;미디어&amp;nbsp;API,&amp;nbsp;스마트폰&amp;nbsp;앱&amp;nbsp;등 &lt;br /&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;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;&lt;i&gt;변환&amp;nbsp;(Transform):&lt;/i&gt; &lt;br /&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;&lt;br /&gt;&amp;nbsp;중복이나&amp;nbsp;오류&amp;nbsp;데이터를&amp;nbsp;제거하고(정제),&amp;nbsp;날짜&amp;nbsp;형식을&amp;nbsp;통일하며, &lt;br /&gt;&lt;br /&gt;&amp;nbsp;민감한&amp;nbsp;개인정보를&amp;nbsp;마스킹(암호화)하는&amp;nbsp;등의&amp;nbsp;작업이&amp;nbsp;이루어진다. &lt;br /&gt;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;&lt;i&gt;적재&amp;nbsp;(Load):&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;잘&amp;nbsp;가공된&amp;nbsp;데이터를&amp;nbsp;데이터&amp;nbsp;웨어하우스(Data&amp;nbsp;Warehouse)나&amp;nbsp;클라우드&amp;nbsp;저장소&amp;nbsp;등&amp;nbsp;최종&amp;nbsp;목적지에&amp;nbsp;저장하여, &lt;br /&gt;&lt;br /&gt;&amp;nbsp;분석가나&amp;nbsp;마케터가&amp;nbsp;언제든&amp;nbsp;꺼내&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;만든다. &lt;br /&gt;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;데이터&amp;nbsp;파이프라인이&amp;nbsp;왜&amp;nbsp;중요한가요? &lt;br /&gt;&lt;br /&gt;수작업&amp;nbsp;및&amp;nbsp;오류&amp;nbsp;감소: &lt;br /&gt;&lt;br /&gt;&amp;nbsp;과거에는&amp;nbsp;개발자나&amp;nbsp;데이터&amp;nbsp;담당자가&amp;nbsp;직접&amp;nbsp;엑셀이나&amp;nbsp;쿼리로&amp;nbsp;데이터를&amp;nbsp;모으고&amp;nbsp;가공했다면, &lt;br /&gt;&lt;br /&gt;&amp;nbsp;파이프라인은&amp;nbsp;이를&amp;nbsp;24시간&amp;nbsp;자동으로&amp;nbsp;처리하여&amp;nbsp;업무&amp;nbsp;효율을&amp;nbsp;높이고&amp;nbsp;휴먼&amp;nbsp;에러를&amp;nbsp;방지. &lt;br /&gt;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;신속한&amp;nbsp;의사결정&amp;nbsp;(Real-time): &lt;br /&gt;&lt;br /&gt;&amp;nbsp;데이터가&amp;nbsp;실시간&amp;nbsp;또는&amp;nbsp;정해진&amp;nbsp;주기마다&amp;nbsp;빠르게&amp;nbsp;업데이트되므로, &lt;br /&gt;&lt;br /&gt;&amp;nbsp;기업은&amp;nbsp;최신&amp;nbsp;데이터를&amp;nbsp;바탕으로&amp;nbsp;즉각적인&amp;nbsp;비즈니스&amp;nbsp;결정을&amp;nbsp;내릴&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;​ &lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;AI와&amp;nbsp;빅데이터의&amp;nbsp;핵심&amp;nbsp;기반:&lt;/b&gt;&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;인공지능&amp;nbsp;모델을&amp;nbsp;학습시키거나&amp;nbsp;비즈니스&amp;nbsp;대시보드를&amp;nbsp;만들&amp;nbsp;때 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;가장&amp;nbsp;중요한&amp;nbsp;것은&amp;nbsp;'깨끗하고&amp;nbsp;믿을&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;데이터'. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;튼튼하고&amp;nbsp;안정적인&amp;nbsp;파이프라인&amp;nbsp;없이는&amp;nbsp;AI&amp;nbsp;도입이나&amp;nbsp;데이터&amp;nbsp;기반&amp;nbsp;의사결정이&amp;nbsp;불가능!!!. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;GUI-tkinter 심화&lt;/b&gt;&lt;/i&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;앞 장에서 tkinter의 기본 위젯과 이벤트 처리 흐름을 익혔다면, 이번 장에서는 GUI 프로그램을 더 체 계적으로 구성하는 방법을 살펴봅니다. 간단한 프로그램은 함수 중심의 절차적 코드만으로도 충분히 만들 수 있습니다. 하지만 버튼 하나, 입력창 하나가 늘어날 때마다 전역 변수가 많아지고, 위젯과 함수가 서로 얽히면서 코드를 읽고 고치 기가 점점 어려워집니다. 기능이 많아질수록 &quot;동작하는 코드&quot;를 넘어 &quot;정리된 코드&quot;를 고민해야 하는 이유입니다. 먼저 절차적 코드가 어디에서 한계를 드러내는지 확인하 고, 이를 객체 지향 프로그래밍(OOP)으로 다시 구성하면 어떤 점이 나아지는지 비교해 봅니다. 이어서 Model(데이터와 비즈니스 로직), View(화면표시와 사용자 입력받기), Presenter (Model과 View를 연결하는 중재자) 로 프로그램의 코드를 역할에 따라 나누는 아키텍처를 가볍게 맛봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 상태와 바인딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI 프로그램을 작성하다 보면 한 번쯤 다음과 같은 고민을 하게 됩니다. &quot;입력창에 적힌 값을 어떻게 가져오지?&quot; &quot;버튼을 누르면 화면의 글자를 어떻게 바꾸지?&quot; 단순한 구조에서는 위젯을 직접 제어하여 대응할 수 있지만, UI가 복잡해질수록 데이터의 흐름과 상 태 변화를 수동으로 추적하는 것은 유지보수를 크게 저하시킵니다. 이 장에서는 이러한 복잡성을 해소할 핵심 개념으로 Tkinter 변수 클래스와 데이터 바인딩(Data Binding)을 다룹니다. 이를 적용하면 위젯을 직접 조작하는 방식이 아니라 데이터의 상태 변화만으 로 화면이 자동 동기화되는 효율적인 프로그램을 작성할 수 있습니다.&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; 상태(State)란? &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태란 한마디로 '프로그램이 지금 이 순간 기억하고 있는 데이터' 를 말합니다. 거창해 보이지만, 우리가 화면에서 보는 거의 모든 것이 상태입니다. 체크박스에 체크가 되어 있는가, 아닌가 사용자가 입력창에 어떤 글자를 적었는가 지금 화면에 보이는 숫자가 몇인가 이런 것들이 모두 프로그램의 '상태'입니다. 프로그램을 만든다는 것은 결국 이 상태를 잘 보관하 고, 잘 바꾸고, 화면에 잘 보여 주는 일 이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 데이터 바인딩이란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는, 우리가 평소에 쓰던 일반 변수로는 상태를 화면과 연결하기가 번거롭다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1784608482597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my_name = &quot;홍길동&quot; # 일반 변수&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;이렇게 만든 변수는 값이 바뀌어도 화면에 그 사실을 스스로 알려 주지 않습니다. 화면을 새 값으 로 바꾸고 싶다면, 우리가 코드로 매번 직접 라벨이나 텍스트를 갱신해 주어야 합니다. 비유하자면, 일반 변수는 &quot;내 값이 바뀌었어!&quot;라고 아무에게도 말하지 않는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조용한 변수 입니다. 데이터 바인딩은 이 불편함을 없애 줍니다. 데이터 바인딩이란 '데이터(변수)'와 '화면(위젯)'을 하 나로 묶어(Bind) 버리는 것 입니다. 한 번 묶어 두면 다음이 자동으로 일어납니다. 변수의 값을 바꾸면 &amp;rarr; 화면이 알아서 바뀐다 화면에서 사용자가 값을 바꾸면 &amp;rarr; 변수 안의 데이터도 알아서 바뀐다 즉, 우리는 화면이 아니라 변수 하나만 신경 쓰면 됩니다. 나머지는 tkinter가 대신 처리해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CDvEJ/dJMcabSuAF9/GXphKHz9IRs3INkCKodJI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CDvEJ/dJMcabSuAF9/GXphKHz9IRs3INkCKodJI0/img.png&quot; data-alt=&quot;상태와 데이터 바인딩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CDvEJ/dJMcabSuAF9/GXphKHz9IRs3INkCKodJI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCDvEJ%2FdJMcabSuAF9%2FGXphKHz9IRs3INkCKodJI0%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;652&quot; height=&quot;325&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상태와 데이터 바인딩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tkinter&lt;/b&gt;&lt;b&gt; 변수 클래스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 바인딩을 하려면, 파이썬의 일반 변수 대신 tkinter 전용 변수 클래스를 써야 합니다. 이 변수들은 값이 바뀔 때마다 화면에 &quot;값이 바뀌었어!&quot;라고 신호를 보내 주는 특별한 변수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sJKoT/dJMcafOfmur/c4CIBLyrWtdiTGt1VzgEDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sJKoT/dJMcafOfmur/c4CIBLyrWtdiTGt1VzgEDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sJKoT/dJMcafOfmur/c4CIBLyrWtdiTGt1VzgEDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsJKoT%2FdJMcafOfmur%2Fc4CIBLyrWtdiTGt1VzgEDk%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;655&quot; height=&quot;316&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.set(), .get() 사용으로 값을 다룸&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#라벨과 변수 묶기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 기존의 변수 사용&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk

root = tk.Tk()
message = tk.StringVar()
message.set(&quot;초기값&quot;)


label = tk.Label(root, text=message.get())
label.pack(padx=20, pady=20)

def change_text():
    message.set(&quot;버튼을 눌러서 바뀐 값!&quot;)  # 이 한 줄만으로 label이 자동 갱신됨

btn = tk.Button(root, text=&quot;값 변경&quot;, command=change_text)
btn.pack()

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiR5Gx/dJMcaa7dOPL/hkulUNf3T2lHUxAIgDXbck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiR5Gx/dJMcaa7dOPL/hkulUNf3T2lHUxAIgDXbck/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiR5Gx/dJMcaa7dOPL/hkulUNf3T2lHUxAIgDXbck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeiR5Gx%2FdJMcaa7dOPL%2FhkulUNf3T2lHUxAIgDXbck%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;122&quot; height=&quot;119&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 변경 버튼을 눌러도, 라벨의 text=message.get()(일반변수)를 사용했기 때문에 처음에 저장한 &quot;초기값&quot;이라는 문자열이 변하지 않는다.&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;textvariable&amp;nbsp;방식&amp;nbsp;(자동&amp;nbsp;갱신됨)&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk

root = tk.Tk()
message = tk.StringVar()
message.set(&quot;초기값&quot;)

# textvariable 방식 (자동 갱신됨)
label = tk.Label(root, textvariable=message)
label.pack(padx=20, pady=20)

def change_text():
    message.set(&quot;버튼을 눌러서 바뀐 값!&quot;)  # 이 한 줄만으로 label이 자동 갱신됨

btn = tk.Button(root, text=&quot;값 변경&quot;, command=change_text)
btn.pack()

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vWsCB/dJMcagGnrxM/gzCi0rq9oFRREEJfermRW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vWsCB/dJMcagGnrxM/gzCi0rq9oFRREEJfermRW0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vWsCB/dJMcagGnrxM/gzCi0rq9oFRREEJfermRW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvWsCB%2FdJMcagGnrxM%2FgzCi0rq9oFRREEJfermRW0%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;171&quot; height=&quot;119&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;textvariable=message 인자를 사용했기 때문에, 값 변경을 누르고 message.set한 문자열이 실시간으로 출력된다.&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;br /&gt;#여러&amp;nbsp;선택지&amp;nbsp;중&amp;nbsp;하나만&amp;nbsp;고르는&amp;nbsp;라디오버튼은&amp;nbsp;IntVar&amp;nbsp;로&amp;nbsp;관리합니다. &lt;br /&gt;#각&amp;nbsp;라디오버튼에&amp;nbsp;value&amp;nbsp;값을&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;있습니다. &lt;br /&gt;#&amp;nbsp;라디오&amp;nbsp;버튼&amp;nbsp;선택에&amp;nbsp;따른&amp;nbsp;라벨&amp;nbsp;실시간&amp;nbsp;출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
def show_result():
    label.config(text=&quot;난이도 : &quot; + level_var.get())

root = tk.Tk()
level_var = tk.StringVar(value=&quot;쉬움&quot;)
for level in [&quot;쉬움&quot;, &quot;보통&quot;, &quot;어려움&quot;]:
    tk.Radiobutton(root, text=level, variable=level_var, value=level,
                   command=show_result).pack(anchor=&quot;w&quot;)
label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/db1CVt/dJMcahSFZw9/AQrLM1xh9EX4s6nkjQ955k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/db1CVt/dJMcahSFZw9/AQrLM1xh9EX4s6nkjQ955k/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/db1CVt/dJMcahSFZw9/AQrLM1xh9EX4s6nkjQ955k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdb1CVt%2FdJMcahSFZw9%2FAQrLM1xh9EX4s6nkjQ955k%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;122&quot; height=&quot;148&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;text는 라디오 버튼 옆에 표시 될 글자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;variable은 k.StringVar(value=&quot;쉬움&quot;) 객체.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value는 해당 라디오 버튼이 선택되었을 때 level_var에 실제로 저장될 값.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;command는 사용자가 라디오버튼 중 하나를 클릭할 때마다 실행될 함수를 지정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;anchor은 라디오버튼을 정렬할 방향.(서쪽)&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;#&quot;OOP 기반 GUI 구조 설계&quot; 의 중요성을 이해하기 위한 단계별 &lt;br /&gt;#&amp;nbsp;To-do&amp;nbsp;list&amp;nbsp;프로그램&amp;nbsp;만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;300x250&quot;)
listbox = tk.Listbox(root)
listbox.pack(fill=&quot;both&quot;, expand=True, padx=10, pady=(0, 7))
entry = tk.Entry(root)
entry.pack(fill=&quot;x&quot;, padx=10, pady=10)
def add_item():
    text = entry.get().strip()
    if text:
        listbox.insert(tk.END, text)
        entry.delete(0, tk.END) #ux를 위해 엔트리를 비워준다
button = tk.Button(root, text=&quot;추가&quot;, command=add_item)
button.pack(pady=(0, 10))

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZ4ZR/dJMcaiD8Sjh/G5ak5TgMcpE8l33Kn2ZVI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZ4ZR/dJMcaiD8Sjh/G5ak5TgMcpE8l33Kn2ZVI0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZ4ZR/dJMcaiD8Sjh/G5ak5TgMcpE8l33Kn2ZVI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZ4ZR%2FdJMcaiD8Sjh%2FG5ak5TgMcpE8l33Kn2ZVI0%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;302&quot; height=&quot;282&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;삭제&amp;nbsp;기능&amp;nbsp;추가&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;300x400&quot;)
listbox = tk.Listbox(root)
listbox.pack(fill=&quot;both&quot;, expand=True, padx=10, pady=(0, 7))

entry = tk.Entry(root)
entry.pack(fill=&quot;x&quot;, padx=10, pady=10)
def add_item():
    text = entry.get().strip()
    if text:
        listbox.insert(tk.END, text)
        entry.delete(0, tk.END)
def delete_item():
    selected_indices = listbox.curselection()
    if selected_indices:
        listbox.delete(selected_indices[0])

add_button = tk.Button(root, text=&quot;추가&quot;, command=add_item)
add_button.pack(pady=(0, 5))

delete_button = tk.Button(root, text=&quot;삭제&quot;, command=delete_item)
delete_button.pack(pady=(0, 10))

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boNrsl/dJMcadbEwTh/v1uHpmysuTSYE2BSD9hnV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boNrsl/dJMcadbEwTh/v1uHpmysuTSYE2BSD9hnV1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boNrsl/dJMcadbEwTh/v1uHpmysuTSYE2BSD9hnV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboNrsl%2FdJMcadbEwTh%2Fv1uHpmysuTSYE2BSD9hnV1%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;302&quot; height=&quot;432&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트박스의 항목을 선택하고 삭제 버튼을 누르면 삭제 되는 기능 추가.&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;완료&amp;nbsp;처리&amp;nbsp;추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;import tkinter as tk

root = tk.Tk()
root.geometry(&quot;300x400&quot;)

total_count = 0
done_count = 0

listbox = tk.Listbox(root)
listbox.pack(fill=&quot;both&quot;, expand=True, padx=10, pady=(0, 7))

entry = tk.Entry(root)
entry.pack(fill=&quot;x&quot;, padx=10, pady=10)

status_label = tk.Label(root, text=&quot;전체 0개 / 완료 0개&quot;)
status_label.pack()

def update_status():
    global total_count, done_count
    status_label.config(text=f&quot;전체 {total_count}개 / 완료 {done_count}개&quot;)

def add_item():
    global total_count
    text = entry.get().strip()
    if text:
        listbox.insert(tk.END, text)
        entry.delete(0, tk.END)
        total_count += 1
        update_status()

def delete_item():
    global total_count
    selected = listbox.curselection()
    if selected:
        listbox.delete(selected[0])
        total_count -= 1
        update_status()

def mark_done():
    global done_count
    selected = listbox.curselection()
        if selected:
            text = listbox.get(selected[0])
            listbox.delete(selected[0])
            listbox.insert(selected[0], &quot;[완료] &quot; + text)
            done_count += 1
            update_status()
def clear_all():
    global total_count, done_count
    listbox.delete(0, tk.END)
    total_count = 0
    done_count = 0
    update_status()

add_button = tk.Button(root, text=&quot;추가&quot;, command=add_item)
add_button.pack()

done_button = tk.Button(root, text=&quot;완료 처리&quot;, command=mark_done)
done_button.pack()

delete_button = tk.Button(root, text=&quot;삭제&quot;, command=delete_item)
delete_button.pack()

clear_button = tk.Button(root, text=&quot;전체 비우기&quot;, command=clear_all)
clear_button.pack()

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4LpoI/dJMcahkZnG2/KkXkNkUbFC4Sjkg9i5iuD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4LpoI/dJMcahkZnG2/KkXkNkUbFC4Sjkg9i5iuD1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4LpoI/dJMcahkZnG2/KkXkNkUbFC4Sjkg9i5iuD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4LpoI%2FdJMcahkZnG2%2FKkXkNkUbFC4Sjkg9i5iuD1%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;302&quot; height=&quot;432&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료한 일정을 표시하는 기능과 전체 삭제 기능 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아따 길다 길어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
절차적 코드의 4가지 한계

#global 키워드의 남발

상태 변수 total_count, done_count가 전역이라, 이 값을 바꾸는 모든 함수가 야 합니다.
위 코드에 global 선언을 해 global 이 다섯 번이나 등장하고, 함수가 늘수록 계속 따라붙습니다.
global 이 많아질수록 어떤 함수가 어떤 상태를 건드리는지 파악하기 어렵습니다.
def mark_done():
    # global done_count  &amp;larr; 이 줄을 빠뜨리면?
    done_count += 1      # 에러는 없지만, 카운터가 증가하지 않습니다.
global 을 깜빡하면 파이썬은 조용히 새 지역 변수를 만들어, 값이 반영되지 않는 버그가 생깁니다.
에러 메시지 없이 동작만 이상해지므로 찾기가 까다롭습니다.
위젯과 변수가 같은 공간에 노출entry , listbox , status_label, 버튼 다섯 개까지
모든 위젯이 전역 공간에 놓여 있습니다.함수도 상태 변수도 같은 공간이라, 프로그램이 커지면 
수십 개의 이름으로 가득 찹니다.이름이 많아지면 충돌 위험도 커집니다. 
무심코 entry 를 다시 쓰면 기존 것이 덮어써져 기능이 망가집니다.
어떤 위젯이 어느 기능에 속하는지 코드 구조만으로는 알 수 없습니다.

#같은 화면을 여러 개 만들 수 없음

가장 본질적인 한계입니다. 위 코드는 &quot;할 일 목록 하나&quot;에 완전히 묶여 있습니다.
한 창에 개인용&amp;middot;업무용 목록을 나란히 두고 싶어도 방법이 없습니다. listbox, total_count,
add_item이 하나씩만 존재하기 때문입니다.

두 번째 목록을 만들려면 listbox2, total_count2처럼 전부 복사해 이름만 바꿔야 하고, 세 개면 또
한 번을 복사합니다.
반면 CounterFrame 같은 클래스는 CounterFrame(root)를 두 번 호출하면 독립된 인스턴스가 
둘 생기고, 각자의 상태와 위젯을 가져 서로 간섭하지 않습니다.

#상태와 동작의 분리

total_count 데이터와 이를 바꾸는 add_item, delete_item, clear_all은 논리적으로 한 덩어리입니다.
하지만 절차적 코드에서는 변수는 위, 함수는 가운데, 위젯은 아래로 흩어져 있습니다.
관련 코드가 떨어져 있으면 기능 하나를 고칠 때 곳곳을 오가야 합니다.
&quot;할 일 목록 로직&quot;만 떼어 재사용하려 해도 경계가 분명하지 않아 추출이 어렵습니다.

정리하며
한계                         핵심 증상
global 남발                   상태 변경 함수마다 선언이 붙고, 빠뜨리면 조용한 버그
이름 공간 오염                  모든 이름이 한 공간에 뒤섞여 충돌 위험  
복제 불가                      같은 화면을 여러 개 만들 수 없음
상태&amp;middot;동작 분리                  관련 코드가 흩어져 수정&amp;middot;재사용이 어려움 

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#OOP&amp;nbsp;기반&amp;nbsp;GUI&amp;nbsp;구조&amp;nbsp;설계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
클래스 도입에 따른 구조적 장점

위젯이 self.label 처럼 객체의 속성이 되어, 어느 메서드에서든 바로 접근할 수 있습니다.
관련 동작이 메서드로 모여, 코드가 기능 단위로 정리됩니다.
화면을 통째로 재사용하거나, 일부만 떼어 테스트하기 쉬워집니다

self란?
클래스 안에서 &quot;자기 자신&quot;을 가리키는 이름입니다. 
self.label 은 &quot;이 객체가 가진
label &quot;이라는 뜻으로, 객체 안 어디서든 같은 위젯을 가리킵니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;Tk&amp;nbsp;직접&amp;nbsp;상속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry(&quot;300x80&quot;)
        self.label = tk.Label(self, text=&quot;안녕하세요&quot;)
        self.label.pack(pady=5)
        tk.Button(self, text=&quot;클릭&quot;, command=self.on_click).pack()
    def on_click(self):
        self.label.config(text=&quot;버튼을 눌렀습니다&quot;)

if __name__ == &quot;__main__&quot;:
    Application().mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯을 self.label 처럼 속성으로 두었기 때문에,on_click 안에서 바로 접근할 수 있 습니다. &lt;br /&gt;위젯을 일일이 전역 변수로 두거나 인자로 주고받을 필요가 없습니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;#Frame&amp;nbsp;상속 &lt;br /&gt;#재사용 가능한 부품으로 캡슐화 실무에서는 Tk를 직접 상속하기보다 Frame 을 상속하는 방식을 더 권장합니다. &lt;br /&gt;#Frame&amp;nbsp;은&amp;nbsp;여러&amp;nbsp;위젯을&amp;nbsp;담는&amp;nbsp;컨테이너이므로,&amp;nbsp;이를&amp;nbsp;상속한&amp;nbsp;클래스는&amp;nbsp;그&amp;nbsp;자체로&amp;nbsp;하나의&amp;nbsp;&quot;부품(컴 &lt;br /&gt;#포넌트)&quot;이&amp;nbsp;됩니다.&amp;nbsp;여러&amp;nbsp;개를&amp;nbsp;조합하거나&amp;nbsp;다른&amp;nbsp;창에&amp;nbsp;끼워&amp;nbsp;넣기&amp;nbsp;쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;import tkinter as tk

class CounterFrame(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.count = 0
        self.display = tk.Label(self, text=&quot;0&quot;, font=(&quot;맑은 고딕&quot;, 24))
        self.display.pack(pady=10)
        tk.Button(self, text=&quot;+1&quot;, command=self.increase).pack()

    def increase(self):
        self.count += 1
        self.display.config(text=str(self.count))

if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    CounterFrame(root).pack(padx=20, pady=20)
    root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1P8ZW/dJMcagGnG5Y/ZS2a7y5Wj8SAF1cJyrzBVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1P8ZW/dJMcagGnG5Y/ZS2a7y5Wj8SAF1cJyrzBVk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1P8ZW/dJMcagGnG5Y/ZS2a7y5Wj8SAF1cJyrzBVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1P8ZW%2FdJMcagGnG5Y%2FZS2a7y5Wj8SAF1cJyrzBVk%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;122&quot; height=&quot;169&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tk.Tk()를 우선 실행하여 root를 만들고, 위젯들을 프레임 단위로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#두번째 예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;import tkinter as tk
class NameInput(tk.Frame):
    def __init__(self, master, on_submit):
        super().__init__(master)
        self.on_submit = on_submit
        self.entry = tk.Entry(self)
        self.entry.pack(side=&quot;left&quot;)
        tk.Button(self, text=&quot;인사하기&quot;, command=self.submit).pack(side=&quot;left&quot;)

    def submit(self):
        self.on_submit(self.entry.get())
class Greeting(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.label = tk.Label(self, text=&quot;&quot;, font=(&quot;맑은 고딕&quot;, 14))
        self.label.pack()
    def show(self, name):
        self.label.config(text=f&quot;안녕하세요, {name}님!&quot;)
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry(&quot;300x120&quot;)
        self.greeting = Greeting(self)
        self.name_input = NameInput(self, on_submit=self.greeting.show)
        self.name_input.pack(pady=10)
        self.greeting.pack(pady=10)
if __name__ == &quot;__main__&quot;:
    App().mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBotq/dJMcafAEbpG/KJkW1RK4QrtPhMgGMP5hgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBotq/dJMcafAEbpG/KJkW1RK4QrtPhMgGMP5hgk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBotq/dJMcafAEbpG/KJkW1RK4QrtPhMgGMP5hgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBotq%2FdJMcafAEbpG%2FKJkW1RK4QrtPhMgGMP5hgk%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;302&quot; height=&quot;152&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#세번째 예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#다중&amp;nbsp;페이지&amp;nbsp;전환 &lt;br /&gt;#페이지를&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;할&amp;nbsp;때,&amp;nbsp;각&amp;nbsp;페이지를&amp;nbsp;Frame&amp;nbsp;으로 &lt;br /&gt;#만들어&amp;nbsp;같은&amp;nbsp;위치에&amp;nbsp;겹쳐&amp;nbsp;둔&amp;nbsp;뒤&amp;nbsp;보여&amp;nbsp;줄&amp;nbsp;페이지만&amp;nbsp;맨&amp;nbsp;위로&amp;nbsp;끌어올리는&amp;nbsp;패턴을&amp;nbsp;자주&amp;nbsp;씁니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
class PageOne(tk.Frame):
    def __init__(self, master, controller):
        super().__init__(master)
        tk.Label(self, text=&quot;첫 번째 페이지&quot;, font=(&quot;맑은 고딕&quot;, 16)).pack(pady=20)
        tk.Button(self, text=&quot;다음으로&quot;,
                  command=lambda: controller.show_page(&quot;PageTwo&quot;)).pack()
class PageTwo(tk.Frame):
    def __init__(self, master, controller):
        super().__init__(master)
        tk.Label(self, text=&quot;두 번째 페이지&quot;, font=(&quot;맑은 고딕&quot;, 16)).pack(pady=20)
        tk.Button(self, text=&quot;이전으로&quot;,
                  command=lambda: controller.show_page(&quot;PageOne&quot;)).pack()
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry(&quot;300x200&quot;)
        self.pages = {}
        for PageClass in (PageOne, PageTwo):
            page = PageClass(self, self)
            self.pages[PageClass.__name__] = page
            page.grid(row=0, column=0, sticky=&quot;nsew&quot;)
        self.show_page(&quot;PageOne&quot;)
    def show_page(self, name):
        self.pages[name].tkraise()
if __name__ == &quot;__main__&quot;:
    App().mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSlQzi/dJMcaae3Fm4/3PoCcZ47UFKHKajlWFazkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSlQzi/dJMcaae3Fm4/3PoCcZ47UFKHKajlWFazkK/img.png&quot; data-alt=&quot;실행시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSlQzi/dJMcaae3Fm4/3PoCcZ47UFKHKajlWFazkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSlQzi%2FdJMcaae3Fm4%2F3PoCcZ47UFKHKajlWFazkK%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;302&quot; height=&quot;232&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wyzoT/dJMcaasA64q/hk9m0XdfltbSbBqgBnx6Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wyzoT/dJMcaasA64q/hk9m0XdfltbSbBqgBnx6Mk/img.png&quot; data-alt=&quot;다음으로 버튼 누를 시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wyzoT/dJMcaasA64q/hk9m0XdfltbSbBqgBnx6Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwyzoT%2FdJMcaasA64q%2Fhk9m0XdfltbSbBqgBnx6Mk%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;302&quot; height=&quot;232&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다음으로 버튼 누를 시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 App 클래스 부분이 이해가 잘 안됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자에서, 부모(tk.Tk)의 생성자를 호출하고,&amp;nbsp; pages라는 페이지들의 객체를 요소로 갖는 딕셔너리를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PageOne, PageTwo 클래스를 묶은 튜플을 순회하여 각 클래스의 생성자를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 page를 pages의 요소로 넣는다. key는 클래스 이름(PageOne, PageTwo), value는 해당 페이지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터 분석</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/10</guid>
      <comments>https://bb0ng2.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 21 Jul 2026 17:12:55 +0900</pubDate>
    </item>
    <item>
      <title>0720</title>
      <link>https://bb0ng2.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;GUI&lt;/b&gt;&lt;/i&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;파이썬 GUI가 필요한 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피지컬 AI(Physical AI)와 tkinter는 한마디로 &lt;span style=&quot;color: #ee2323;&quot;&gt;인공지능이&amp;nbsp;탑재된&amp;nbsp;물리적&amp;nbsp;기기(로봇,&amp;nbsp;센서&amp;nbsp;등)를&amp;nbsp;조종하고&amp;nbsp;모니터링&amp;nbsp;하는&amp;nbsp;데스크톱&amp;nbsp;리모컨&lt;/span&gt;을 만드는 과정.&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;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;피지컬 AI (두뇌 + 감각/근육):&lt;/b&gt;&lt;br /&gt;카메라, 마이크, 온습도 센서 등을 통해 현실 세계의 데이터를 수집.&lt;br /&gt;이를 AI 모델(머신러닝 등)로 분석한 뒤&lt;br /&gt;물리적인 행동(로봇 팔 이동, 모터 작동 등)을 수행하거나 데이터를 반환.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tkinter (사용자 소통 창구):&lt;/b&gt;&lt;br /&gt;사람이 피지컬 AI의 상태를 눈으로 확인하고,&lt;br /&gt;명령을 내릴 수 있도록&lt;br /&gt;버튼, 그래프, 텍스트 입력창 등 GUI(그래픽 사용자 인터페이스)를 그려준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 대표적인 활용 사례&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tkinter는 파이썬에 기본 내장되어 있어 별도 설치가 필요 없고 매우 가볍기 때문에,&lt;br /&gt;피지컬 AI 연구나 하드웨어 제어 프로토타입을 만들 때 널리 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로봇 제어 패널 (HMI):&lt;/b&gt;&lt;br /&gt;로봇의 현재 위치, 센서 값, 배터리 상태를 tkinter 창에 띄우고,&lt;br /&gt;창에 있는 방향키 버튼을 눌러 로봇을 원격 조종한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;엣지 디바이스(초소형 기기)의 터치스크린:&lt;/b&gt;&lt;br /&gt;라즈베리파이(Raspberry Pi) 같은 소형 컴퓨터에 센서와 AI 모델을 올린 뒤,&lt;br /&gt;작은 LCD 디스플레이에 tkinter로 만든 UI를 전체 화면으로 띄워&lt;br /&gt;스마트 기기(음성 인식기, 스마트 도어록 등)처럼 작동하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실시간 AI 모니터링:&lt;/b&gt;&lt;br /&gt;웹캠이 인식한 사람의 행동(비전 AI)이나&lt;br /&gt;마이크로 들어온 음성 데이터 분석 결과를&lt;br /&gt;tkinter 창에 텍스트나 그래프로 실시간 업데이트하여 보여준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 한계와 대안&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피지컬 AI 시스템에 tkinter를 사용할 때 반드시 고려해야 할 가장 큰 한계는&lt;br /&gt;&lt;b&gt;'데스크톱 로컬 환경'에 묶인다는 점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 출처 : &lt;a href=&quot;https://cafe.naver.com/f-e/cafes/15757610/articles/15333?menuid=328&amp;amp;referrerAllArticles=false&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://cafe.naver.com/f-e/cafes/15757610/articles/15333?menuid=328&amp;amp;referrerAllArticles=false&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;&lt;i&gt;&lt;b&gt;tkinter&lt;/b&gt;&lt;/i&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;tkinter는 파이썬에서 GUI 프로그램을 만들 때 사용하는 표준 모듈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI 창, 버튼, 라벨, 입력창 같은 요소들이 모두 이 모듈 안에 포함&lt;i&gt;&lt;b&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#창 띄우기&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import tkinter as tk

#tk,Tk() 는 GUI 프로그램의 기본 창
# 보통 루트 창(root Window)
root=tk.Tk()
root.title(&quot;tkinter로 파이썬 GUI 배우기&quot;)
root.geometry(&quot;200x500+100+300&quot;)# +연산자를 사용해서 창 시작 위치도 설정 가능&quot;가로x세로+x좌표+y좌표&quot;
root.configure(bg=&quot;white&quot;) # 창의 배경색 설정 bg는 백그라운드
#root.resizable(False, False)# 가로/세로 창 크기 변경 막아놓는 함수
#창 크기 변경 자체는 허용하되, 너무 작아지거나 너무 커지는 것을 막고 싶을 수 있습니다. 최소 창 크기와 최대 창 크기 지정 가능
#root.minsize(300, 200)
#root.maxsize(800, 600)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 윈도우를 띄우는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#라벨&amp;nbsp;(어디에,&amp;nbsp;어떤&amp;nbsp;글자를&amp;nbsp;표시하겠다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;label =tk.Label(root, text='라벨입니다.') #첫 인자로 root를 넣어 어떤 윈도우에 포함되는지 명시 &lt;br /&gt;label.pack()# root와 label을 패킹&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;label = tk.Label(
root,
text=&quot;Python GUI&quot;,
font=(&quot;맑은 고딕&quot;, 16),
fg=&quot;white&quot;,         #폰트 색
bg=&quot;navy&quot;           #배경 색
)
label.pack(padx=40,pady=10)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨 함수의 여러 인자들.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#버튼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;def hello():
    label.config(text=&quot;clicked button&quot;,fg=&quot;red&quot;)

button = tk.Button(root, text=&quot;click!&quot;, command=hello)
button.pack()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;text&amp;nbsp;옵션은&amp;nbsp;버튼에&amp;nbsp;표시할&amp;nbsp;글자입니다. &lt;br /&gt;command&amp;nbsp;옵션은&amp;nbsp;버튼을&amp;nbsp;클릭했을&amp;nbsp;때&amp;nbsp;실행할&amp;nbsp;함수를&amp;nbsp;지정합니다. &lt;br /&gt;config()&amp;nbsp;는&amp;nbsp;이미&amp;nbsp;만들어진&amp;nbsp;위젯의&amp;nbsp;옵션을&amp;nbsp;변경할&amp;nbsp;때&amp;nbsp;사용합니다. &lt;br /&gt;여기서는&amp;nbsp;라벨의&amp;nbsp; &lt;br /&gt;text&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;의미 &lt;br /&gt;command=hello&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;버튼을&amp;nbsp;클릭했을&amp;nbsp;때&amp;nbsp;hello&amp;nbsp;함수&amp;nbsp;실행 &lt;br /&gt;command=hello()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;프로그램&amp;nbsp;시작&amp;nbsp;시&amp;nbsp;hello()를&amp;nbsp;바로&amp;nbsp;실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#mainloop()&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;root.mainloop()
print(&quot;파이썬 핵심 요약 학습에 오신 것을 환영합니다!&quot;)

'''
mainloop()의 역할
# 콘솔 프로그램은 보통 위에서 아래로 실행된 뒤 종료.
하지만 GUI 프로그램은 조금 다르게 동작
# GUI 프로그램은 사용자가 어떤 행동을 할 떄까지 계속 기다린다.
#이러한 사용자의 동작을 이벤트라고 한다.
예) 창 닫기/ 버튼 클릭 / 키보드 입력/ 마우스 이동/ 창 크기 변경

tkinter 프로그램 세 단계
1. tkinter 모듈 가져오기
2. 기본 창 생성하기
3. mainloop()로 창 유지하기
-------------------------------------
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tkinter&amp;nbsp;프로그램&amp;nbsp;workflow &lt;br /&gt;&lt;br /&gt;창&amp;nbsp;생성 &lt;br /&gt;⬇️ &lt;br /&gt;위젯&amp;nbsp;배치 &lt;br /&gt;⬇️ &lt;br /&gt;mainloop()&amp;nbsp;실행 &lt;br /&gt;⬇️ &lt;br /&gt;사용자&amp;nbsp;이벤트&amp;nbsp;대기 &lt;br /&gt;⬇️ &lt;br /&gt;이벤트&amp;nbsp;발생&amp;nbsp;시&amp;nbsp;처리 &lt;br /&gt;⬇️ &lt;br /&gt;창을&amp;nbsp;닫을&amp;nbsp;때까지&amp;nbsp;반복&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#버튼&amp;nbsp;옵션과&amp;nbsp;상태적용&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
btn1 = tk.Button(root, text=&quot;두꺼운 테두리&quot;, bd=5, relief=&quot;raised&quot;, width=15)
# relief는 입체감이라는 뜻
# flat:평평함,sunken:화면 안으로 음푹 들어간 모양, groove: 테두리 선만 안으로 파인 모양, ridge: 테두리 선만 밖으로 튀어나온 모양,
# solid: 단순한 검은색 단선 테두리 사용 가능
btn1.pack(pady=5)
btn2 = tk.Button(root, text=&quot;커서 변경 (손가락)&quot;, cursor=&quot;hand2&quot;, bg=&quot;lightyellow&quot;)
#cursor에 쓸 수 있는 다른 유용한 옵션들
#&quot;arrow&quot; 기본 화살표 모양, &quot;cross&quot;:십자선 모양(도면이나 그래프에서 위치를 지정할 때 유용
#&quot;watch&quot; : 시계 또는 모래시계 모양(로딩중이거나 작업중임을 나타낼 때 사용) &quot;xterm&quot;: 텍스트 입력 칸에서 흔히 보이는 'I'모양의 텍스트 커서
btn2.pack(pady=5)
btn3 = tk.Button(root, text=&quot;클릭 불가&quot;, state=&quot;disabled&quot;)
btn3.pack(pady=5)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;123&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfPFl8/dJMcagzvKry/XL8mU0PwvDb1hqRwoBGcH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfPFl8/dJMcagzvKry/XL8mU0PwvDb1hqRwoBGcH0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfPFl8/dJMcagzvKry/XL8mU0PwvDb1hqRwoBGcH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfPFl8%2FdJMcagzvKry%2FXL8mU0PwvDb1hqRwoBGcH0%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;123&quot; height=&quot;146&quot; data-origin-width=&quot;123&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#엔트리&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;'''
주요 옵션
옵션                      허용값                               설명

width   0 이상의 정수 (입력 문자 개수 기준)                  너비를 입력 문자 개수 기준으로 설정
font    (&quot;맑은 고딕&quot;, 12) 형식의 튜플 또는 문자열             입력 글자의 글꼴, 크기, 스타일을 설정
fg      색상 이름( &quot;black&quot; ) 또는 16진수( &quot;#000000&quot; )       입력 글자의 색상을 설정
bg      색상 이름( &quot;white&quot; ) 또는 16진수( &quot;#ffffff&quot; )       입력 상자의 배경색을 설정
show    단일 문자 ( &quot;*&quot; 등)                                입력한 문자를 다른 문자로 표시(주로 비밀번호 입력창)
state   &quot;normal&quot; , &quot;disabled&quot; , &quot;readonly&quot;중 하나         입력 가능 상태를 설정
'''
# 비밀번호 입력 엔트리 만들기 예제
import tkinter as tk
root = tk.Tk()
password_entry = tk.Entry(root, show=&quot;*&quot;, width=30)
password_entry.pack(pady=20)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ohuNN/dJMcaglXHu7/1oyGAfzk4538KsCcTGhg01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ohuNN/dJMcaglXHu7/1oyGAfzk4538KsCcTGhg01/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ohuNN/dJMcaglXHu7/1oyGAfzk4538KsCcTGhg01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FohuNN%2FdJMcaglXHu7%2F1oyGAfzk4538KsCcTGhg01%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;216&quot; height=&quot;91&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;텍스트&amp;nbsp;사용하기&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
text = tk.Text(root)
text.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUw2U/dJMcadvVEVO/C1Pviq9TBcrgIldCpX2ns1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUw2U/dJMcadvVEVO/C1Pviq9TBcrgIldCpX2ns1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUw2U/dJMcadvVEVO/C1Pviq9TBcrgIldCpX2ns1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUw2U%2FdJMcadvVEVO%2FC1Pviq9TBcrgIldCpX2ns1%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;566&quot; height=&quot;368&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모장 만들기가 이렇게 쉽다니... 윈플 실습 6-1은 지옥이었는데,,,&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;'''
메서드                         설명                          사용 예
get()               특정 범위의 텍스트 가져오기             text.get(&quot;1.0&quot;, tk.END)
delete()            특정 범위의 텍스트 지우기               text.delete(&quot;1.0&quot;, tk.END)
insert()            특정 위치에 텍스트 넣기                 text.insert(&quot;1.0&quot;, &quot;안녕&quot;)


옵션                          허용값                                  설명
width                0 이상의 정수 (글자 수 기준)                     위젯의 너비를 '글자 수' 기준으로 설정
height               0 이상의 정수 (글자 수 기준)                     위젯의 높이를 '줄 수' 기준으로 설정
font                 (&quot;Arial&quot;, 12) 형식의 튜플 또는 문자열            입력 글자의 글꼴, 크기 설정
bg                  색상 이름(&quot;white&quot;) 또는 16진수(&quot;#ffffff&quot; )         배경색을 설정
fg                  색상 이름(&quot;white&quot;) 또는 16진수(&quot;#ffffff&quot; )          글자색을 설정
wrap                &quot;word&quot; , &quot;char&quot; , &quot;none&quot; 중 하나                   텍스트가 위젯 너비를 넘어갈 때 줄바꿈 방식 설정

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#checkButton&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import tkinter as tk
def show_result():
    if agree_var.get():
        label.config(text=&quot;동의했습니다.&quot;)

root = tk.Tk()
agree_var = tk.BooleanVar()
check = tk.Checkbutton(root, text=&quot;동의합니다&quot;, variable=agree_var, command=show_result)
check.pack(padx=20, pady=20)
label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOgNce/dJMcagM8Oxe/o94hBzxlTe1ZzHjaR5dyz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOgNce/dJMcagM8Oxe/o94hBzxlTe1ZzHjaR5dyz0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOgNce/dJMcagM8Oxe/o94hBzxlTe1ZzHjaR5dyz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOgNce%2FdJMcagM8Oxe%2Fo94hBzxlTe1ZzHjaR5dyz0%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;130&quot; height=&quot;138&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                     설명
select()                체크 상태로 변경
deselect()              체크 해제 상태로 변경
toggle()                현재 상태를 반대로 변경
invoke()                사용자가 클릭한 것처럼 동작 실행

옵션                      허용 값                            설명                  
text                    임의의 문자열                     체크 버튼 옆에 표시할 문자열 설정
variable                Tkinter 변수 객체                체크 상태를 저장할 변수 설정  
onvalue                 정수 1 , 문자열 &quot;yes&quot; 등          체크되었을 때 변수에 저장할 값 설정
offvalue                정수 0 , 문자열 &quot;no&quot; 등           체크 해제되었을 때 변수에 저장할 값 설정
command                 함수 객체                        체크 상태가 바뀔 때 실행할 함수 설정
state                   &quot;normal&quot; , &quot;disabled&quot; 중 하나    위젯의 상태 설정

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#radiobutton&amp;nbsp;사용&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
food = tk.IntVar(value=1)
tk.Radiobutton(root, text=&quot;육고기&quot;, variable=food, value=1).pack()
tk.Radiobutton(root, text=&quot;해산물&quot;, variable=food, value=2).pack()
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라디오버튼은 하나만 선택할 수 있는 체크박스이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLfSsw/dJMcaiYkcP6/LgwgbWW7W2eNxDih6uWCSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLfSsw/dJMcaiYkcP6/LgwgbWW7W2eNxDih6uWCSK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLfSsw/dJMcaiYkcP6/LgwgbWW7W2eNxDih6uWCSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLfSsw%2FdJMcaiYkcP6%2FLgwgbWW7W2eNxDih6uWCSK%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;122&quot; height=&quot;82&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                     설명
select()            라디오 버튼을 선택
deselect()          라디오 버튼의 선택 해제
invoke()            사용자가 클릭한 것처럼 동작 실행
주요 옵션
옵션                                  허용 값                                설명
text                                임의의 문자열                         라디오 버튼 옆에 표시할 문자열 설정
variable                            Tkinter 변수 객체                    선택값을 저장할 변수 설정
value                               변수 타입에 맞는 값                    라디오 버튼을 선택했을 때 변수에 저장할 값 설정
command                             함수 객체                            라디오 버튼을 선택했을 때 실행할 함수 설정
state                               &quot;normal&quot; , &quot;disabled&quot; 중 하나        위젯의 상태 설정

알아두기
Radiobutton은 같은 변수를 공유해야 합니다
여러 개의 Radiobutton이 하나의 그룹으로 동작하려면 모두 같은 variable을 사용해야 합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#리스트박스&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
def show_selected(event):
    label.config(text=&quot;선택한 항목: &quot; + listbox.get(listbox.curselection()))
root = tk.Tk()
listbox = tk.Listbox(root, height=5)
listbox.pack(padx=10, pady=10)

for item in [&quot;Python&quot;, &quot;Java&quot;, &quot;JavaScript&quot;, &quot;C&quot;, &quot;C++&quot;]:
    listbox.insert(tk.END, item)
listbox.bind(&quot;&amp;lt;&amp;lt;ListboxSelect&amp;gt;&amp;gt;&quot;, show_selected)# 해당 항목에서 이벤트가 발생하면 특정 함수를 호출한다.
label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDNktI/dJMcadpcUc2/kGeYq42R8epVjVBspmzKWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDNktI/dJMcadpcUc2/kGeYq42R8epVjVBspmzKWK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDNktI/dJMcadpcUc2/kGeYq42R8epVjVBspmzKWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDNktI%2FdJMcadpcUc2%2FkGeYq42R8epVjVBspmzKWK%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;166&quot; height=&quot;177&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;177&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;'''
메서드                     설명                                사용 예
insert()            항목 추가                        listbox.insert(tk.END, &quot;Python&quot;)
delete()            항목 삭제                        listbox.delete(0)
get()               항목 가져오기                     listbox.get(0)
curselection()      선택된 항목의 인덱스 가져오기        listbox.curselection()
size()              전체 항목 개수 가져오기             listbox.size()
selection_set()     특정 항목 선택                    listbox.selection_set(0)
selection_clear()   선택 해제                       listbox.selection_clear(0, tk.END)

주요옵션
옵션                              허용 값                                        설명
width                0 이상의 정수 (글자 수 기준)                             위젯의 너비를 글자 수 기준으로 설정
height               0 이상의 정수 (줄 수 기준)                              표시할 항목의 줄 수 설정
selectmode          &quot;browse&quot; , &quot;single&quot; , &quot;multiple&quot; ,&quot;extended&quot; 중 하나   항목 선택 방식을 설정
font                (&quot;Arial&quot;, 12) 형식의 튜플 또는 문자열                     목록 글자의 글꼴과 크기 설정
fg                  색상 이름( &quot;black&quot; ) 또는 16진수( &quot;#000000&quot; )            글자색 설정
bg                  색상 이름( &quot;white&quot; ) 또는 16진수( &quot;#ffffff&quot; )            배경색 설정

Listbox의 인덱스

Listbox의 항목 위치는 0부터 시작합니다. 첫 번째 항목은 0, 두 번째 항목은 1, 세 번째 항목은 2
입니다.
listbox.get(0)
위 코드는 Listbox의 첫 번째 항목을 가져옵니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#스핀&amp;nbsp;박스&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
def show_value():
    label.config(text=&quot;선택한 수량: &quot; + spinbox.get())
root = tk.Tk()
root.title(&quot;Spinbox 예제&quot;)
spinbox = tk.Spinbox(root, from_=1, to=10, width=10)
spinbox.pack(padx=10, pady=10)
button = tk.Button(root, text=&quot;확인&quot;, command=show_value)
button.pack(pady=5)
label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스핀박스는 수량을 조절할 수 있는 박스이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UpxFg/dJMcahyrW32/a1Lm2CXWG8pdY5rccBnTak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UpxFg/dJMcahyrW32/a1Lm2CXWG8pdY5rccBnTak/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UpxFg/dJMcahyrW32/a1Lm2CXWG8pdY5rccBnTak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUpxFg%2FdJMcahyrW32%2Fa1Lm2CXWG8pdY5rccBnTak%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;122&quot; height=&quot;148&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#문자열을&amp;nbsp;사용한&amp;nbsp;스핀박스&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
def show_month():
    label.config(text=&quot;선택한 달: &quot; + spinbox.get())
root = tk.Tk()
root.title(&quot;문자열 Spinbox&quot;)
#1월 부터 12월까지 선택할 수 있는 스핀박스
spinbox = tk.Spinbox(root, values=(&quot;1월&quot;, &quot;2월&quot;, &quot;3월&quot;, &quot;4월&quot;, &quot;5월&quot;, &quot;6월&quot;, &quot;7월&quot;, &quot;8월&quot;, &quot;9월&quot;, &quot;10월&quot;, &quot;11월&quot;, &quot;12월&quot;), width=10)
spinbox.pack(padx=10, pady=10)

#선택된 값을 확인하는 버튼
button = tk.Button(root, text=&quot;확인&quot;, command=show_month)
button.pack(pady=5)

label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k2lYW/dJMcadW6qcw/L6PRpJuUpy3DaFLy3p4D60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k2lYW/dJMcadW6qcw/L6PRpJuUpy3DaFLy3p4D60/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k2lYW/dJMcadW6qcw/L6PRpJuUpy3DaFLy3p4D60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk2lYW%2FdJMcadW6qcw%2FL6PRpJuUpy3DaFLy3p4D60%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;122&quot; height=&quot;148&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                     설명
get()               현재 값 가져오기
delete()            입력된 값 삭제
insert()            값 삽입

주요 옵션

옵션                              허용 값                                        설명
from_                       실수 또는 정수                                   시작값 설정
to                          실수 또는 정수                                   끝값 설정
increment                   0보다 큰 실수 또는 정수                           값이 증가하거나 감소하는 단위 설정
values                      문자열 튜플 또는 리스트                           선택할 문자열 목록 설정
width                       0 이상의 정수 (글자 수 기준)                      위젯의 너비 설정
command                     함수 객체                                       위아래 버튼을 눌러 값이 바뀔 때 실행할 함수 설정
state                       &quot;normal&quot; , &quot;readonly&quot; , &quot;disabled&quot;중 하나       입력 가능 상태 설정
�
�
 알아두기
Spinbox는 사용자가 값을 직접 입력할 수 있다는 점에서 Entry와 비슷합니다. 하지만
Spinbox는 정해진 범위나 목록 안에서 값을 선택하도록 만들 수 있다는 차이가 있습니다.
예를 들어 단순히 이름을 입력받을 때는 Entry가 적합하고, 수량이나 월처럼 제한된 값을 선
택할 때는 Spinbox가 적합합니다.

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;스케일&amp;nbsp;사용&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
def show_value():
    label.config(text=&quot;선택한 볼륨: &quot; + str(scale.get()))
root = tk.Tk()
scale = tk.Scale(root, from_=0, to=100, orient=&quot;horizontal&quot;)
scale.pack(padx=10, pady=10)
button = tk.Button(root, text=&quot;확인&quot;, command=show_value)
button.pack(pady=5)
label = tk.Label(root, text=&quot;&quot;)
label.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;볼륨을 조절하는 스케일.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qIbUN/dJMcaf8n3ir/I8shdmZD0ru7lTKNtKnaY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qIbUN/dJMcaf8n3ir/I8shdmZD0ru7lTKNtKnaY1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qIbUN/dJMcaf8n3ir/I8shdmZD0ru7lTKNtKnaY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqIbUN%2FdJMcaf8n3ir%2FI8shdmZD0ru7lTKNtKnaY1%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;128&quot; height=&quot;171&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 밝기 세로 조절 예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
def show_value():
    label.config(text=f&quot;선택한 밝기: {scale.get()}&quot;)
root = tk.Tk()
scale = tk.Scale(root, from_=0, to=100, orient=&quot;vertical&quot;)
scale.pack(pady=10)
tk.Button(root, text=&quot;확인&quot;, command=show_value).pack()
label = tk.Label(root)
label.pack(pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFBwh3/dJMcahyrW5J/YqkU2ry5izaWKB1SsAkKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFBwh3/dJMcahyrW5J/YqkU2ry5izaWKB1SsAkKKk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFBwh3/dJMcahyrW5J/YqkU2ry5izaWKB1SsAkKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFBwh3%2FdJMcahyrW5J%2FYqkU2ry5izaWKB1SsAkKKk%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;122&quot; height=&quot;225&quot; data-origin-width=&quot;122&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드                                              
메서드                                                  설명
get()                                            현재 Scale 값 가져오기
set()                                            Scale 값을 지정한 값으로 변경하기

주요 옵션

옵션                                      허용 값                                설명
from_                               실수 또는 정수                            시작값 설정
to                                  실수 또는 정수                            끝값 설정
orient                              &quot;horizontal&quot; , &quot;vertical&quot; 중 하나        Scale 방향 설정
length                              0 이상의 정수 (픽셀 단위)                  Scale의 길이 설정
resolution                          0보다 큰 실수 또는 정수                    값이 움직이는 간격 설정
tickinterval                        0 이상의 실수 또는 정수                    눈금 표시 간격 설정
label                               임의의 문자열                             Scale위젯에 표시할 제목 설정
command                             함수 객체                               값이 바뀔 때 실행할 함수 설정
state                               &quot;normal&quot; , &quot;disabled&quot; 중 하나          사용 가능 상태 설정

 알아두기
위젯마다 값을 가져오는 방식이 다릅니다
Checkbutton과 Radiobutton은 직접 위젯에서 값을 가져오기보다 연결된 변수에서 값을 가
져오는 방식이 일반적입니다.
반면 Listbox , Scale , Spinbox는 위젯의 get() 메서드를 사용하여 값을 가져올 수 있습
니다.
단, Listbox는 먼저 curselection()으로 선택된 항목의 위치를 확인한 뒤, 그 위치를 이용해
값을 가져와야 합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
관리자                         배치 방식                       어울리는 상황
pack()              한 방향으로 차곡차곡 쌓음                  단순한 세로/가로 나열
grid()              행&amp;middot;열 표 형태로 배치                        입력 폼, 설정 화면
place()             좌표로 직접 고정                           정밀한 고정 배치(특수한 경우)
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#pack()&amp;nbsp;레이아웃&amp;nbsp;예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;200x150&quot;)
tk.Label(root, text=&quot;첫 번째 라벨 위젯&quot;, bg=&quot;red&quot;).pack()
tk.Label(root, text=&quot;두 번째 라벨 위젯&quot;, bg=&quot;green&quot;).pack()
tk.Label(root, text=&quot;세 번째 라벨 위젯&quot;, bg=&quot;yellow&quot;).pack()
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xtGHi/dJMcacYcjEY/prtpeqpMft596SgDiChdJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xtGHi/dJMcacYcjEY/prtpeqpMft596SgDiChdJ0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xtGHi/dJMcacYcjEY/prtpeqpMft596SgDiChdJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxtGHi%2FdJMcacYcjEY%2FprtpeqpMft596SgDiChdJ0%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;202&quot; height=&quot;182&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;'''
주요 옵션
옵션                                  허용 값                                설명
side                    &quot;top&quot; , &quot;bottom&quot; , &quot;left&quot; , &quot;right&quot;             위젯을 붙일 방향
fill                    &quot;x&quot; , &quot;y&quot; , &quot;both&quot; , &quot;none&quot;                     남는 공간을 채우는 방향
expand                  True , False                                    남는 공간 차지 여부
anchor                  &quot;n&quot; , &quot;s&quot; , &quot;e&quot; , &quot;w&quot; , &quot;ne&quot; , &quot;center&quot; 등      할당된 공간 안에서의 정렬
padx / pady             정수 또는 (좌, 우) / (상, 하) 튜플                  위젯 바깥 여백
ipadx / ipady           정수                                             위젯 안쪽 여백

side가 &quot;공간을 어디서 떼어올지&quot;를 정한다면, anchor는 &quot;그 공간 안에서 어느 쪽으로 정
렬할지&quot;를 정합니다
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#side&amp;nbsp;예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;400x300&quot;)
tk.Button(root, text=&quot;LEFT&quot;).pack(side=&quot;left&quot;)
tk.Button(root, text=&quot;RIGHT&quot;).pack(side=&quot;right&quot;)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnUTkL/dJMcadCFBIF/vbHOR91UvigF3DFGYl8V6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnUTkL/dJMcadCFBIF/vbHOR91UvigF3DFGYl8V6k/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnUTkL/dJMcadCFBIF/vbHOR91UvigF3DFGYl8V6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnUTkL%2FdJMcadCFBIF%2FvbHOR91UvigF3DFGYl8V6k%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;402&quot; height=&quot;332&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#fill과&amp;nbsp;expand&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;250x120&quot;)
tk.Button(root, text=&quot;가로만 채우기&quot;).pack(fill=&quot;x&quot;)
tk.Button(root, text=&quot;전체 확장&quot;).pack(expand=True, fill=&quot;both&quot;)
root.mainloop()
'''
위젯 숨기기
btn.pack_forget()
btn.pack()
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfFJv5/dJMcafUYaiO/Z6cQa1o5KBAkqSbjgog0Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfFJv5/dJMcafUYaiO/Z6cQa1o5KBAkqSbjgog0Ak/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfFJv5/dJMcafUYaiO/Z6cQa1o5KBAkqSbjgog0Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfFJv5%2FdJMcafUYaiO%2FZ6cQa1o5KBAkqSbjgog0Ak%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;252&quot; height=&quot;152&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#grid()&amp;nbsp;레이아웃&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;180x50&quot;)
tk.Label(root, text=&quot;이름&quot;).grid(row=0, column=0)
tk.Entry(root).grid(row=0, column=1)
tk.Label(root, text=&quot;나이&quot;).grid(row=1, column=0)
tk.Entry(root).grid(row=1, column=1)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo5ven/dJMcagM8OSY/6d5UyCgD0CtNGl8KGQ3wGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo5ven/dJMcagM8OSY/6d5UyCgD0CtNGl8KGQ3wGk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo5ven/dJMcagM8OSY/6d5UyCgD0CtNGl8KGQ3wGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo5ven%2FdJMcagM8OSY%2F6d5UyCgD0CtNGl8KGQ3wGk%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;182&quot; height=&quot;82&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 옵션
옵션                      허용값                                         설명
row                     0 이상의 정수                                    위젯이 놓일 행 번호(0부터)
column                  0 이상의 정수                                    위젯이 놓일 열 번호(0부터)
columnspan              1 이상의 정수                                    위젯이 가로로 여러 칸 차지
rowspan                 1 이상의 정수                                    위젯이 세로로 여러 칸 차지
sticky                  &quot;n&quot; , &quot;s&quot; , &quot;e&quot; , &quot;w&quot; 및 조합(&quot;nsew&quot; 등)        셀 안에서 위젯을 붙일 방향

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#span과&amp;nbsp;sticky&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.grid_columnconfigure(1, weight=1) #1번 열만 늘어남
opt = {&quot;padx&quot;: 5, &quot;pady&quot;: 5}
tk.Label(root, text=&quot;이메일 주소&quot;, bg=&quot;lightblue&quot;).grid(row=1, column=0, sticky=&quot;w&quot;, **opt)
tk.Entry(root, bg=&quot;lightyellow&quot;).grid(row=1, column=1, sticky=&quot;we&quot;, **opt)
tk.Button(root, text=&quot;로그인&quot;, bg=&quot;lightgreen&quot;).grid(row=2, columnspan=2, sticky=&quot;we&quot;, **opt)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVu98Y/dJMcahkYEuP/vg2YN3XRKlqv55WyEg0knK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVu98Y/dJMcahkYEuP/vg2YN3XRKlqv55WyEg0knK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVu98Y/dJMcahkYEuP/vg2YN3XRKlqv55WyEg0knK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVu98Y%2FdJMcahkYEuP%2Fvg2YN3XRKlqv55WyEg0knK%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;236&quot; height=&quot;99&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#선생님이 주신 예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import tkinter as tk

root = tk.Tk()
root.geometry(&quot;250x150&quot;)

# 첫 번째 줄: 아주 긴 텍스트를 넣어 0번 열(Column)의 너비를 넓힘
lbl_long=tk.Label(root,text=&quot;이 라벨 때문에 격자의 너비가 넓어집니다.&quot;,bg=&quot;lightgray&quot;)
lbl_long.grid(row=0, column=0,pady=5)

# 두 번째 줄: 서쪽(왼쪽)에 달라붙는 버튼
btn_west = tk.Button(root,text=&quot;서쪽(w)에 붙음&quot;)
btn_west.grid(row=1, column=0, sticky=&quot;w&quot;, pady=5)

# 세 번째 줄: 동쪽(오른쪽)에 달라붙는 버튼
btn_east = tk.Button(root,text=&quot;동쪽에 붙음&quot;)
btn_east.grid(row=2, column=0, sticky=&quot;e&quot;, pady=5)

# 네 번째 줄: 가로로 꽉 채우는 버튼
btn_fill = tk.Button(root,text=&quot;가로로 꽉 채움(ew)&quot;)
btn_fill.grid(row=3, column=0, sticky=&quot;ew&quot;, pady=5)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deiKPh/dJMb99UGE86/zdazk3bcIzp47TerCjEJV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deiKPh/dJMb99UGE86/zdazk3bcIzp47TerCjEJV0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deiKPh/dJMb99UGE86/zdazk3bcIzp47TerCjEJV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeiKPh%2FdJMb99UGE86%2Fzdazk3bcIzp47TerCjEJV0%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;252&quot; height=&quot;182&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#행&amp;nbsp;열&amp;nbsp;늘리기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk

root = tk.Tk()
root.title(&quot;반응형 창 만들기&quot;)
root.geometry(&quot;300x200&quot;)

#0번째 행과 0번째 열이 창 크기 변화에 반응하여 늘어나도록 가중치(weight) 부여
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

#텍스트 창 생성
text_widget = tk.Text(root,bg=&quot;lightyellow&quot;)

#넓어지는 격자(0행 0열) 안에서 동서남북(nsew)으로 꽉 채우도록 (sticky) 배치
text_widget.grid(row=0, column=0,sticky=&quot;nsew&quot;,padx=10,pady=10)

#하단 닫기 버튼(1번째 행에 배치. weight를 주지 않았으므로 크기 고정됨)
btn_close = tk.Button(root,text=&quot;닫기&quot;, command=root.quit)
btn_close.grid(row=1, column=0, pady=(0,10))

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6711p/dJMcaccOvKx/BWH2cl21RIBgxHXU6C5wT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6711p/dJMcaccOvKx/BWH2cl21RIBgxHXU6C5wT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6711p/dJMcaccOvKx/BWH2cl21RIBgxHXU6C5wT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6711p%2FdJMcaccOvKx%2FBWH2cl21RIBgxHXU6C5wT0%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;302&quot; height=&quot;232&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3MLI9/dJMcadJtYUz/jLgmsI4tvXc0f1MXwzyxh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3MLI9/dJMcadJtYUz/jLgmsI4tvXc0f1MXwzyxh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3MLI9/dJMcadJtYUz/jLgmsI4tvXc0f1MXwzyxh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3MLI9%2FdJMcadJtYUz%2FjLgmsI4tvXc0f1MXwzyxh0%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;584&quot; height=&quot;232&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창의 크기를 바꿈에 따라 문자열의 줄바꿈도 바뀜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;'''
메서드
grid_forget()
grid_remove()
위젯을 떼어내고 위치 정보도 잊음
설명
위젯을 떼어내되 위치 정보를 기억(다시 
grid() 호출 시 원래 자리로)
'''
#예제
import tkinter as tk
def toggle_widget():
    #winfo_ismapped()로 현재 화면에 보이는지 확인
    if text_box.winfo_ismapped():
        #숨길 때는 grid_remove() 사용 (위치 기억)
        text_box.grid_remove()
        btn_toggle.config(text=&quot;텍스트 창 보이기&quot;)
    else:
        #다시 나타낼 때는 아무 옵션 없이 grid()만 호출해도 원래 자리(row=1,column=0)로 돌아옴!
        text_box.grid()
        btn_toggle.config(text=&quot;텍스트 창 숨기기&quot;)

root =tk.Tk()
root.geometry(&quot;250x150&quot;)

btn_toggle = tk.Button(root,text= &quot;텍스트 창 숨기기&quot;,command=toggle_widget)
btn_toggle.grid(row=0, column=0, pady=10, padx=10)


text_box=tk.Text(root,height=3,width=20,bg=&quot;lightyellow&quot;)
text_box.insert(&quot;1.0&quot;, &quot;잠시 숨겨질 수 있는 \n수필 텍스트 입니다.&quot;)
#최초 배치 시 위치와 여백 설정 (이 설정을 grid_remove가 기억함)
text_box.grid(row=1, column=0, padx=20)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLJ37U/dJMcaiqyk1g/udbDhk9byWkO2RPAhlZukk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLJ37U/dJMcaiqyk1g/udbDhk9byWkO2RPAhlZukk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLJ37U/dJMcaiqyk1g/udbDhk9byWkO2RPAhlZukk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLJ37U%2FdJMcaiqyk1g%2FudbDhk9byWkO2RPAhlZukk%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;252&quot; height=&quot;182&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omjSM/dJMcafOeTZe/FM0sWSL7BrNOY1LQLSp1OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omjSM/dJMcafOeTZe/FM0sWSL7BrNOY1LQLSp1OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omjSM/dJMcafOeTZe/FM0sWSL7BrNOY1LQLSp1OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FomjSM%2FdJMcafOeTZe%2FFM0sWSL7BrNOY1LQLSp1OK%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;252&quot; height=&quot;182&quot; data-origin-width=&quot;252&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숨길수 있는 텍스트박스&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;#place()&amp;nbsp;레이아웃 &lt;br /&gt;#place()&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;배치에만&amp;nbsp;제한적으로&amp;nbsp;사용합니다. &lt;br /&gt;#예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;300x150&quot;)
tk.Label(root, text=&quot;절대 위치 (50, 30)&quot;, bg=&quot;lightblue&quot;).place(x=50, y=30) # 절대 좌표 배
tk.Label(root, text=&quot;상대 좌표 배치 (정중앙)&quot;, bg=&quot;lightgreen&quot;).place(
relx=0.5, rely=0.5, anchor=&quot;center&quot;) # 상대 좌표 배치 (relx, rely는 0.0 ~ 1.0 비율)

tk.Label(root, text=&quot;상대 크기 지정 (너비 80%)&quot;, bg=&quot;lightyellow&quot;).place(
relx=0.1, rely=0.8, relwidth=0.8
) # 상대 크기 지정 (relwidth, relheight)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2UJc/dJMcagM8O0L/BnKZrkf4zcUDYPOLQtGkhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2UJc/dJMcagM8O0L/BnKZrkf4zcUDYPOLQtGkhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2UJc/dJMcagM8O0L/BnKZrkf4zcUDYPOLQtGkhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2UJc%2FdJMcagM8O0L%2FBnKZrkf4zcUDYPOLQtGkhK%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;302&quot; height=&quot;182&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg6Vop/dJMcahea1EW/rY3VVz0i9oD3Z8F6mwsYM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg6Vop/dJMcahea1EW/rY3VVz0i9oD3Z8F6mwsYM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg6Vop/dJMcahea1EW/rY3VVz0i9oD3Z8F6mwsYM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg6Vop%2FdJMcahea1EW%2FrY3VVz0i9oD3Z8F6mwsYM0%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;297&quot; height=&quot;471&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창을 늘림에따라 위치가 바뀌는 상대좌표배치와, 바뀌지 않는 절대 위치&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 옵션
옵션                          허용 값                                설명
x , y                       정수(픽셀)                             절대 좌표(왼쪽 위 모서리 기준)
relx , rely                 0.0~1.0 실수                          부모 크기 대비 비율
anchor                      &quot;n&quot;, &quot;s&quot;, &quot;e&quot;, &quot;w&quot;, &quot;ne&quot;, &quot;center&quot;등  기준점 위치
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#pack()&amp;nbsp;과&amp;nbsp;grid()&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
레이아웃 관리자 혼용 규칙
같은 부모 위젯 안에서 
pack() 과 grid() 를 섞어 쓰면 안 됩니다.
두 관리자가 요구하는 창 크기가 충돌하면서 tkinter가 크기를 계속 재계산해, 창이 떨리거나 응답하지 않는 상태가 됩니다.
이때 명확한 예외(TclError )가 발생하지 않는 경우가 많아 원인을 찾기 어렵습니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#Frame으로&amp;nbsp;계층&amp;nbsp;분리 &lt;br /&gt;#Frame&amp;nbsp;으로&amp;nbsp;구역을&amp;nbsp;나누면&amp;nbsp;됩니다. &lt;br /&gt;#핵심은&amp;nbsp;&quot;같은&amp;nbsp;부모&amp;nbsp;안에서만&amp;nbsp;한&amp;nbsp;가지&amp;nbsp;관리자를&amp;nbsp;쓴다&quot;&amp;nbsp;는&amp;nbsp;것입니다.&amp;nbsp;부모가&amp;nbsp;다르면(창&amp;nbsp;vs.&amp;nbsp;프레임) &lt;br /&gt;#서로&amp;nbsp;다른&amp;nbsp;관리자를&amp;nbsp;써도&amp;nbsp;괜찮습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;#예제
import tkinter as tk
root = tk.Tk()
root.geometry(&quot;360x150&quot;)
root.configure(bg=&quot;#797777&quot;)
# 위쪽 프레임: 입력 폼 (내부는 grid)
top = tk.Frame(root, bg=&quot;#d7ecff&quot;, padx=5, pady=5)
top.pack(side=&quot;top&quot;, fill=&quot;x&quot;, padx=5, pady=5)
tk.Label(top, text=&quot;이메일 주소&quot;, bg=&quot;#d7ecff&quot;).grid(row=0, column=0, sticky=&quot;w&quot;, padx=10)
tk.Entry(top, bg=&quot;white&quot;).grid(row=0, column=1, sticky=&quot;we&quot;)
top.grid_columnconfigure(1, weight=1)  # 입력칸이 가로로 늘어나도록
# 아래쪽 프레임: 버튼 (내부는 pack)
bottom = tk.Frame(root, bg=&quot;#ffe0e0&quot;, padx=5, pady=5)
bottom.pack(side=&quot;bottom&quot;, fill=&quot;x&quot;, padx=5, pady=5)
tk.Button(bottom, text=&quot;확인&quot;, bg=&quot;#7bc96f&quot;, fg=&quot;white&quot;, width=8).pack(side=&quot;left&quot;)
tk.Button(bottom, text=&quot;취소&quot;, bg=&quot;#e87a7a&quot;, fg=&quot;white&quot;, width=8).pack(side=&quot;right&quot;)
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brZNvs/dJMcajpnn0i/yI9QPbWV3vmNpsiBJ10fDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brZNvs/dJMcajpnn0i/yI9QPbWV3vmNpsiBJ10fDK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brZNvs/dJMcajpnn0i/yI9QPbWV3vmNpsiBJ10fDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrZNvs%2FdJMcajpnn0i%2FyI9QPbWV3vmNpsiBJ10fDK%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;362&quot; height=&quot;182&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#이벤트&amp;nbsp;기반&amp;nbsp;프로그래밍&amp;nbsp;예제&amp;nbsp;한글은&amp;nbsp;2바이트&amp;nbsp;이므로&amp;nbsp;조금씩&amp;nbsp;늦음&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
def on_key(event):
    result_label.config(text=f&quot;입력된 키 : {event.char}&quot;)
root = tk.Tk()
entry = tk.Entry(root, width=30)
entry.pack(padx=20, pady=10)
result_label = tk.Label(root, text=&quot;입력된 키 : &quot;, bg=&quot;lightyellow&quot;)
result_label.pack(padx=20, pady=10)
entry.bind(&quot;&amp;lt;Key&amp;gt;&quot;, on_key)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QPrXY/dJMcag0w4ga/F5qTVjPdLvyNNBMkl91ckK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QPrXY/dJMcag0w4ga/F5qTVjPdLvyNNBMkl91ckK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QPrXY/dJMcag0w4ga/F5qTVjPdLvyNNBMkl91ckK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQPrXY%2FdJMcag0w4ga%2FF5qTVjPdLvyNNBMkl91ckK%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;256&quot; height=&quot;112&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한글 '똥'은 2바이트라 실시간으로 반영되지 않는 모습&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
분류                                       시퀀스                                     설명
마우스                                 &amp;lt;Button-1&amp;gt;                                      좌클릭
마우스                                 &amp;lt;Button-2&amp;gt;                                      휠 클릭
마우스                                 &amp;lt;Button-3&amp;gt;                                      우클릭
마우스                                 &amp;lt;ButtonRelease-1&amp;gt;                               버튼 뗌
마우스                                 &amp;lt;B1-Motion&amp;gt;                                     좌클릭 드래그
마우스                                 &amp;lt;Double-Button-1&amp;gt;                               더블클릭
마우스                                 &amp;lt;Enter&amp;gt; / &amp;lt;Leave&amp;gt;                               위젯 영역 진입 / 이탈
키보드                                 &amp;lt;KeyPress&amp;gt;                                      모든 키
키보드                                 &amp;lt;KeyPress-Return&amp;gt;                               엔터
키보드                                 &amp;lt;Key-Escape&amp;gt;                                    ESC
키보드                                 &amp;lt;Control-c&amp;gt;                                     Ctrl+C 조합
키보드                                 &amp;lt;a&amp;gt;                                             일반 문자키 (간단 표기)
윈도/포커스                              &amp;lt;Configure&amp;gt;                                    크기&amp;middot;위치 변경
윈도/포커스                              &amp;lt;FocusIn&amp;gt; / &amp;lt;FocusOut&amp;gt;                         포커스 획득 / 상실
윈도/포커스                              &amp;lt;Destroy&amp;gt;                                      위젯 소멸

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
메뉴
Menu는 프로그램 창 상단에 보이는 메뉴바입니다. 메모장에서 보이는 파일 / 편집 / 보기 같은
것들이 바로 Menu입니다.
큰 메뉴바 안에 작은 메뉴(파일, 편집 등)를 넣고, 그 안에 실제 항목(열기, 저장 등)을 넣는 계층
구조를 갖습니다.

메뉴 만드는 순서

메뉴바 역할을 할 Menu바를 생성합니다.
그 안에 넣을 하위 메뉴(Menu)를 생성합니다.
하위 메뉴는 add_command 등으로 항목을 채웁니다.
add_cascade로 하위 메뉴를 메뉴바에 연결합니다.
root.config(menu=...)로 메뉴바를 창에 부착합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#메뉴&amp;nbsp;사용&amp;nbsp;예제&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;400x120&quot;)

menubar = tk.Menu(root)# 1) 메뉴바 생성

file_menu = tk.Menu(menubar, tearoff=0)# 2) 하위 메뉴 생성
file_menu.add_command(label=&quot;열기&quot;, command=lambda: print(&quot;열기 클릭&quot;)) # 3) 항목 채우기
file_menu.add_separator()
file_menu.add_command(label=&quot;종료&quot;, command=root.destroy)


menubar.add_cascade(label=&quot;파일&quot;, menu=file_menu)# 4) 메뉴바에 연결
root.config(menu=menubar)               # 5) 창에 부착
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnSwxO/dJMcabkKuMW/NHsnyz8WnSqHxX8pQaffBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnSwxO/dJMcabkKuMW/NHsnyz8WnSqHxX8pQaffBK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnSwxO/dJMcabkKuMW/NHsnyz8WnSqHxX8pQaffBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnSwxO%2FdJMcabkKuMW%2FNHsnyz8WnSqHxX8pQaffBK%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;402&quot; height=&quot;172&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                             설명
add_command(label, command)                        클릭하면 동작하는 일반 항목 추가
add_cascade(label, menu)                           하위 메뉴를 펼치는 항목 추가 (메뉴바에 메뉴 연결) 
add_separator()                                    항목 사이에 구분선 추가
add_checkbutton(label, variable)                   켜고 끌 수 있는 체크 항목 추가
add_radiobutton(label, variable, value)            여러 개 중 하나만 선택하는 항목 추가        
tk_popup(x, y)                                     지정한 화면 좌표에 메뉴를 팝업으로 표시
delete(index)                                      항목 삭제

주요 옵션                     
옵션                                허용 값                        설명
tearoff                 0 , 1 (또는 False /True )
label                   문자열
command                 함수(콜러블)                             항목 클릭 시 실행할 함수
accelerator             문자열(예:&quot;Ctrl+S&quot; )                     단축키 안내 문자열
state                   tk.NORMAL , tk.DISABLED ,tk.ACTIVE      항목 상태
underline               0 이상의 정수(글자 인덱스)                 항목 이름 중 한 글자에 밑줄을 그어 키보드 접근 문자로 지정
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.geometry(&quot;300x120&quot;)
menu = tk.Menu(root, tearoff=0)
menu.add_command(label=&quot;복사&quot;, command=lambda: print(&quot;복사&quot;))
menu.add_command(label=&quot;붙여넣기&quot;, command=lambda: print(&quot;붙여넣기&quot;))
label = tk.Label(root, text=&quot;이 글자 위에서 오른쪽 클릭&quot;, bg=&quot;lightyellow&quot;)
label.pack(pady=30)
label.bind(&quot;&amp;lt;Button-3&amp;gt;&quot;, lambda e: menu.tk_popup(e.x_root, e.y_root))
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brmXWl/dJMcah6lwrJ/i8zEPVr7k0cDpSpz2jpQCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brmXWl/dJMcah6lwrJ/i8zEPVr7k0cDpSpz2jpQCK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brmXWl/dJMcah6lwrJ/i8zEPVr7k0cDpSpz2jpQCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrmXWl%2FdJMcah6lwrJ%2Fi8zEPVr7k0cDpSpz2jpQCK%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;302&quot; height=&quot;152&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
�
알아두기
팝업 좌표는 왜 x_root 일까?
tk_popup() 은 화면 전체를 기준으로 한 절대 좌표를 받습니다. 그래서 위젯 기준인 
e.y 가 아니라 e.x_root , e.y_root 를 넘겨야 클릭한 자리에 정확히 메뉴가 뜹니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
Dialogs는 사용자와 잠깐 대화하기 위해 뜨는 팝업창입니다.
tkinter는 자주 쓰는 대화상자를 미리 만들어 제공하므로, 창을 직접 디자인하지 않고 함수 한 줄
로 호출할 수 있습니다.
대화상자는 크게 두 종류로 나뉩니다.

표준 대화상자

tkinter가 제공하는 
messagebox , filedialog , simpledialog 가 있습니다.
사용자 정의 대화상자
Toplevel 로 대화상자로 입력 필드가 여러 개거나 디자인을 짜야 할 때 직접 만드는 창입니다.

표준 대화상자

세 가지 주요 모듈
표준 대화상자는 용도에 따라 세 모듈로 나뉩니다.
모듈                               용도                            주요 메서드
messagebox                      알림, 경고, 예/아니오 질문        showinfo , askyesno            
filedialog                      파일/폴더 열기&amp;middot;저장 창           askopenfilename , asksaveasfilename           
simpledialog                    짧은 텍스트&amp;middot;숫자 입력받기          askstring , askinteger          

세 모듈 모두 tkinter 안에 있지만, 자동으로 따라오지 않으므로 별도로 import 해야 합니다.

from tkinter import messagebox, filedialog, simpledialog

대화상자는 &quot;값을 반환한다&quot;

표준 대화상자의 중요한 특징은 사용자의 선택&amp;middot;입력 결과를 반환값으로 돌려준다는 점입니다.
따라서 보통 결과 = 대화상자함수(...) 형태로 받아서 사용합니다.
모듈                              반환값                             취소했을 때
messagebox                      True / False                        보통 False
filedialog                      파일 경로 문자열                       빈 문자열 &quot;&quot;
simpledialog                    입력한 텍스트/숫자                      None

사용자가 취소하면 정상적인 값 대신 None이나 빈 문자열이 돌아옵니다. 반환값을 그대로 쓰기
전에 반드시 확인하는 습관이 필요합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#messagebox&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#가장&amp;nbsp;자주&amp;nbsp;쓰는&amp;nbsp;모듈로&amp;nbsp;정보를&amp;nbsp;보여주거나&amp;nbsp;예/아니오를&amp;nbsp;묻습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
def exit_app():
    if messagebox.askyesno(&quot;확인&quot;, &quot;정말 종료할까요?&quot;):
        root.destroy()
tk.Button(root, text=&quot;종료&quot;, command=exit_app).pack(padx=180, pady=48)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5B8ki/dJMcadCFBR2/RPUDr9Upzzr48ypKsuJeG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5B8ki/dJMcadCFBR2/RPUDr9Upzzr48ypKsuJeG1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5B8ki/dJMcadCFBR2/RPUDr9Upzzr48ypKsuJeG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5B8ki%2FdJMcadCFBR2%2FRPUDr9Upzzr48ypKsuJeG1%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;397&quot; height=&quot;154&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUT3ES/dJMcadCFBSa/3BntbKDWRfkacRkVurkLFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUT3ES/dJMcadCFBSa/3BntbKDWRfkacRkVurkLFk/img.png&quot; data-alt=&quot;종료를 누르면.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUT3ES/dJMcadCFBSa/3BntbKDWRfkacRkVurkLFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUT3ES%2FdJMcadCFBSa%2F3BntbKDWRfkacRkVurkLFk%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;202&quot; height=&quot;152&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;종료를 누르면.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
messagebox 주요 메서드
메서드                                         설명
showinfo                                      정보 알림 표시 (확인 버튼)   
showwarning                                  경고 알림 표시 (확인 버튼)
showerror                                    오류 알림 표시 (확인 버튼)
askquestion                                  예/아니오 질문, &quot;yes&quot; /&quot;no&quot; 문자열 반환       
askyesno                                     예/아니오 질문, True /False 반환
askokcancel                                  확인/취소 질문, True /False 반환
askretrycancel                               재시도/취소 질문, True /False 반환
askyesnocancel                               예/아니오/취소 질문, True /False /None 반환

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#filedialog&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#파일이나&amp;nbsp;폴더의&amp;nbsp;경로를&amp;nbsp;사용자에게&amp;nbsp;받아옵니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from tkinter import filedialog
path = filedialog.askopenfilename(
title=&quot;파일 선택&quot;,
filetypes=[(&quot;텍스트 파일&quot;, &quot;*.txt&quot;), (&quot;모든 파일&quot;, &quot;*.*&quot;)]
)
if path:
    print(&quot;선택한 파일:&quot;, path)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VsOp1/dJMcac4WAj1/zYWZYEGeDvNw9eVVMiuKek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VsOp1/dJMcac4WAj1/zYWZYEGeDvNw9eVVMiuKek/img.png&quot; data-alt=&quot;실행결과. 파일탐색기를 연다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VsOp1/dJMcac4WAj1/zYWZYEGeDvNw9eVVMiuKek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsOp1%2FdJMcac4WAj1%2FzYWZYEGeDvNw9eVVMiuKek%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;946&quot; height=&quot;533&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행결과. 파일탐색기를 연다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;'''

filedialog 주요 메서드

메서드                                                 설명
askopenfilename                                     파일 하나 열기. 선택한 경로 문자열 반환 (취소 시 &quot;&quot; )
askopenfilenames                                    여러 파일 열기. 경로 튜플 반환 (취소 시 빈 튜플)
asksaveasfilename                                   저장할 경로 받기. 경로 문자열 반환 (취소 시 &quot;&quot; )                                   
askdirectory                                        폴더 선택. 폴더 경로 문자열 반환 (취소 시 &quot;&quot; )
askopenfile                                         파일을 열어 읽기용 파일 객체 반환 (취소 시 None )
asksaveasfile                                       파일을 열어 쓰기용 파일 객체 반환 (취소 시 None )

filedialog 주요 옵션

옵션                            허용                                       값설명
title                         문자열                                     대화상자 제목
initialdir                    폴더 경로 문자열                             처음 열릴 폴더 경로                   
filetypes                     (설명, 패턴) 튜플의 리스트 (예 : &quot;*.txt&quot;))]    표시할 파일 종류 제한    
defaultextension              확장자 문자열 (예: &quot;.txt&quot; )                  저장 시 자동으로 붙는 기본확장자
initialfile                   파일명 문자열                               저장 대화상자에 미리 채워질 파일명
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#simpledialog &lt;br /&gt;#짧은&amp;nbsp;텍스트나&amp;nbsp;숫자를&amp;nbsp;입력받을&amp;nbsp;때&amp;nbsp;사용합니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from tkinter import simpledialog
name = simpledialog.askstring(&quot;입력&quot;, &quot;이름을 입력하세요.&quot;)
age = simpledialog.askinteger(&quot;입력&quot;, &quot;나이를 입력하세요.&quot;, minvalue=0, maxvalue=120)
if name:
    print(f&quot;{name}님, {age}세&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8HJCF/dJMb99NRT8M/yFYZ85iObeCkmFqdwMP8Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8HJCF/dJMb99NRT8M/yFYZ85iObeCkmFqdwMP8Zk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8HJCF/dJMb99NRT8M/yFYZ85iObeCkmFqdwMP8Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8HJCF%2FdJMb99NRT8M%2FyFYZ85iObeCkmFqdwMP8Zk%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;182&quot; height=&quot;118&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sVKEV/dJMcaf1DWGF/6hXEuHDXZWAW9a7twCe5e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sVKEV/dJMcaf1DWGF/6hXEuHDXZWAW9a7twCe5e0/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sVKEV/dJMcaf1DWGF/6hXEuHDXZWAW9a7twCe5e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsVKEV%2FdJMcaf1DWGF%2F6hXEuHDXZWAW9a7twCe5e0%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;182&quot; height=&quot;118&quot; data-origin-width=&quot;182&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
simpledialog 주요 메서드

메서드               설명
askstring         문자열 입력받기. 입력한 문자열 반환 (취소 시 None )
askinteger        정수 입력받기. minvalue , minvalue , maxvalue 로 범위 제한 가능 (취소 시 None )
askfloat          실수 입력받기. maxvalue 로 범위 제한 가능 (취소 시 None )

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#사용자&amp;nbsp;정의&amp;nbsp;대화상자&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
messagebox 나 filedialog 는 정해진 모양만 제공합니다.
입력 필드가 여러 개 필요하거나 디자인을 직접 짜고 싶다면 새 창을 직접 만들어야 하며, 이때
사용하는 것이 Toplevel 위젯입니다.

Toplevel이란?

Tk() 가 프로그램의 메인 창이라면, 메인 창처럼 Toplevel() 은 그 위에 추가로 띄우는 새 창입니다.
Label , Entry , Button 등 모든 위젯을 넣을 수 있습니다.
메인 창에 종속된 자식 창이므로, 메인 창을 닫으면 함께 닫힙니다.
비 모달 방식 대화상자
메인 창과 동시에 떠 있으며 둘 다 자유롭게 조작할 수 있는 방식을 비 모달(modeless) 이라고 합
니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
root.title(&quot;메인 창&quot;)
root.geometry(&quot;300x200&quot;)
def open_window():
    win = tk.Toplevel(root) # 새 창 생성
    win.title(&quot;새 창&quot;)
    tk.Label(win, text=&quot;이건 새 창입니다!&quot;).pack(pady=20)
    tk.Button(win, text=&quot;닫기&quot;, command=win.destroy).pack()
tk.Button(root, text=&quot;새 창 열기&quot;, command=open_window).pack(pady=50)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxHaAe/dJMcagTQV6c/XUqgf8oY96MBj0GME3xGiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxHaAe/dJMcagTQV6c/XUqgf8oY96MBj0GME3xGiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxHaAe/dJMcagTQV6c/XUqgf8oY96MBj0GME3xGiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxHaAe%2FdJMcagTQV6c%2FXUqgf8oY96MBj0GME3xGiK%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;302&quot; height=&quot;232&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL9ccF/dJMcagTQV7P/Kj2mEWuJW96U0i6RHKOb0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL9ccF/dJMcagTQV7P/Kj2mEWuJW96U0i6RHKOb0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL9ccF/dJMcagTQV7P/Kj2mEWuJW96U0i6RHKOb0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL9ccF%2FdJMcagTQV7P%2FKj2mEWuJW96U0i6RHKOb0k%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;453&quot; height=&quot;381&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#모달(Modal)&amp;nbsp;대화상자&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
사용자가 입력을 끝낼 때까지 메인 창을 막아두는 방식을 모달(Modal) 창이라 합니다.
설정 변경이나 필수 입력처럼 반드시 응답을 받아야 할 때 사용합니다.
모달은 다음 세 가지 설정으로 만듭니다.

메서드                                     설명
transient(root)                         메인 창에 종속시킴 (항상 위에 표시)
grab_set()                              모든 입력을 이 창에만 집중
wait_window(dialog)                     이 창이 닫힐 때까지 코드 실행을 멈추고 대기
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
사용자가 입력을 끝낼 때까지 메인 창을 막아두는 방식을 모달(Modal) 창이라 합니다.
설정 변경이나 필수 입력처럼 반드시 응답을 받아야 할 때 사용합니다.
모달은 다음 세 가지 설정으로 만듭니다.

메서드                                     설명
transient(root)                         메인 창에 종속시킴 (항상 위에 표시)
grab_set()                              모든 입력을 이 창에만 집중
wait_window(dialog)                     이 창이 닫힐 때까지 코드 실행을 멈추고 대기
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJiGEk/dJMcajwhhfS/uXou5r0tBIYP5tq4MHr56k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJiGEk/dJMcajwhhfS/uXou5r0tBIYP5tq4MHr56k/img.png&quot; data-alt=&quot;이름 입력을 완료할때까지 메인 창은 꺼지지 않는다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJiGEk/dJMcajwhhfS/uXou5r0tBIYP5tq4MHr56k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJiGEk%2FdJMcajwhhfS%2FuXou5r0tBIYP5tq4MHr56k%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;480&quot; height=&quot;129&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이름 입력을 완료할때까지 메인 창은 꺼지지 않는다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#캔버스&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
도형 그리기 (Shapes)

선, 사각형, 원 그리기

Canvas는 다양한 도형을 그리는 create_ 로 시작하는 메서드를 제공합니다.
각 도형은 고유한 ID(정수)를 반환하며, 이를 통해 나중에 도형을 수정할 수 있습니다.
원(Oval)은 해당 원을 둘러싸는 가상의 직사각형(Bounding Box)의 좌측 상단 좌표와 우측 하단
좌표를 입력하여 그립니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#선,&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;달리하여&amp;nbsp;출력합니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=250, bg=&quot;#F8F3C0&quot;, highlightthickness=0)
canvas.pack()
canvas.create_line(50, 50, 350, 50, fill=&quot;#6194FA&quot;, width=7)
canvas.create_rectangle(50, 90, 150, 190, fill=&quot;#F58C9F&quot;, width=1)
canvas.create_oval(200, 90, 300, 190, fill=&quot;#88EBD4&quot;, width=1, dash=(5, 2))
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n17WL/dJMcabdPO5R/zznTFyhKWMIfOyKFsXgVuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n17WL/dJMcabdPO5R/zznTFyhKWMIfOyKFsXgVuk/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n17WL/dJMcabdPO5R/zznTFyhKWMIfOyKFsXgVuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn17WL%2FdJMcabdPO5R%2FzznTFyhKWMIfOyKFsXgVuk%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;402&quot; height=&quot;282&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                                 설명
create_line(x1, y1, x2, y2, ...)                    직선 그리기
create_rectangle(x1, y1, x2, y2, ...)               직사각형 그리기
create_oval(x1, y1, x2, y2, ...)                    타원이나 원 그리기
create_polygon(x1, y1, x2, y2, x3, y3, ...)         3개 이상의 점을 연결한 다각형 그리기

주요 옵션
옵션                              허용                              값설명
fill            색상 이름( &quot;red&quot; ) 또는 16진수 값( &quot;#ff0000&quot; )       도형 내부를 채울 색상
outline         색상 이름( &quot;black&quot; ) 또는 16진수 값( &quot;#000000&quot; )     도형의 테두리 색상
width           정수 또는 실수 (픽셀 단위, 예: 2 , 1.5 )              선이나 테두리의 두께
dash            정수 튜플(예: (5, 3) ) 또는 문자열(예:&quot;.&quot; , &quot;-&quot; )     점선 스타일 지정 (예 : (5, 3)은 5픽셀 긋고3픽셀 띄움)
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#캔버스에&amp;nbsp;그리기&amp;nbsp;예제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk

old_x,old_y =None,None

def start_draw(event):
    global old_x,old_y
    old_x,old_y = event.x, event.y
def draw_line(event):
    global old_x,old_y
    if old_x and old_y:
        canvas.create_line(old_x, old_y, event.x, event.y, width=2,fill=&quot;black&quot;)
        old_x,old_y = event.x, event.y
root = tk.Tk()

canvas=tk.Canvas(root,width=400,height=300,bg=&quot;white&quot;,cursor=&quot;crosshair&quot;)
canvas.pack()

canvas.bind(&quot;&amp;lt;Button-1&amp;gt;&quot;, start_draw)
canvas.bind(&quot;&amp;lt;B1-Motion&amp;gt;&quot;, draw_line)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bayIM2/dJMcacYcjVH/PnnurKsPN7eqJ734xH2IP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bayIM2/dJMcacYcjVH/PnnurKsPN7eqJ734xH2IP1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bayIM2/dJMcacYcjVH/PnnurKsPN7eqJ734xH2IP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbayIM2%2FdJMcacYcjVH%2FPnnurKsPN7eqJ734xH2IP1%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;406&quot; height=&quot;336&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
텍스트 및 이미지 삽입

캔버스에 텍스트와 사진 출력하기

Canvas 상의 특정 좌표를 지정하여 문자열을 렌더링할 수 있습니다.
PhotoImage 객체를 활용하여 이미지를 삽입할 수 있습니다.
이미지는 가비지 컬렉터(Garbage Collector)에 의해 삭제되지 않도록 전역 변수로 관리하거나 객
체에 참조를 남겨야 합니다.

문구와 이미지 출력하기

캔버스 중앙에 텍스트를 표시하고, 이미지 파일이 있으면 출력하며 없으면 안내 문구로 대체합니다.
코드가 저장된 동일한 경로에 200x200 크기의 'image.png' 파일을 위치해 둡니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg=&quot;white&quot;)
canvas.pack()
canvas.create_text(200, 50, text=&quot;Canvas 입니다.&quot;, font=(&quot;Arial&quot;, 15, &quot;bold&quot;), fill=&quot;purple&quot;)
try:
    img = tk.PhotoImage(file=&quot;image.png&quot;)
    canvas.image = img
    canvas.create_image(200, 180, image=img, anchor=&quot;center&quot;)
except tk.TclError:
    canvas.create_text(200, 180, text=&quot;(이미지가 없습니다.)&quot;)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyUJeX/dJMcafm9bjZ/vLKY6jOwl4Bc2aG9hIy3F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyUJeX/dJMcafm9bjZ/vLKY6jOwl4Bc2aG9hIy3F1/img.png&quot; data-alt=&quot;고양이그림으로 저장햇는데 잘렸네..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyUJeX/dJMcafm9bjZ/vLKY6jOwl4Bc2aG9hIy3F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyUJeX%2FdJMcafm9bjZ%2FvLKY6jOwl4Bc2aG9hIy3F1%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;406&quot; height=&quot;336&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;고양이그림으로 저장햇는데 잘렸네..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
객체 제어 및 애니메이션

도형 이동 및 속성 변경하기

Canvas에 그려진 모든 항목은 고유 식별자(ID) 또는 사용자가 지정한 문자열 태그(Tag)로 제어할
수 있습니다.
move() 메서드와 after() 메서드를 결합하면 간단한 애니메이션 효과를 구현할 수 있습니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#버튼&amp;nbsp;클릭으로&amp;nbsp;원을&amp;nbsp;오른쪽으로&amp;nbsp;이동시키기 &lt;br /&gt;#버튼을&amp;nbsp;누르면&amp;nbsp;파란&amp;nbsp;원이&amp;nbsp;50ms&amp;nbsp;간격으로&amp;nbsp;오른쪽으로&amp;nbsp;움직이다가&amp;nbsp;화면&amp;nbsp;끝에&amp;nbsp;닿으면&amp;nbsp;멈춥니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
def move_circle():
    canvas.move(&quot;my_circle&quot;, 5, 0)
    coords = canvas.coords(&quot;my_circle&quot;)
    if coords[2] &amp;lt; 400:
        root.after(50, move_circle)
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=200, bg=&quot;white&quot;)
canvas.pack()

canvas.create_oval(10, 80, 50, 120, fill=&quot;blue&quot;, tags=&quot;my_circle&quot;)

btn = tk.Button(root, text=&quot;이동 시작&quot;, command=move_circle)
btn.pack(pady=10)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVrtoY/dJMcagfdHWl/kjlwbLzphWnh2wfaVhvnq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVrtoY/dJMcagfdHWl/kjlwbLzphWnh2wfaVhvnq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVrtoY/dJMcagfdHWl/kjlwbLzphWnh2wfaVhvnq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVrtoY%2FdJMcagfdHWl%2FkjlwbLzphWnh2wfaVhvnq1%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;406&quot; height=&quot;282&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ri4np/dJMcadpcUCb/m3GUG5jtunWhzXEjPapkCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ri4np/dJMcadpcUCb/m3GUG5jtunWhzXEjPapkCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ri4np/dJMcadpcUCb/m3GUG5jtunWhzXEjPapkCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fri4np%2FdJMcadpcUCb%2Fm3GUG5jtunWhzXEjPapkCk%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;406&quot; height=&quot;282&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동 시작을 누르면 캔버스 끝까지 이동함.&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                             설명
move(id_or_tag, dx, dy)                         객체를 현재 위치에서 dx, dy만큼 이동
coords(id_or_tag, [x1,y1...])                   객체의 현재 좌표를 반환하거나 새로운 좌표로 재설정
itemconfig(id_or_tag,options)                   생성된 객체의 옵션(색상, 두께 등)을 동적으로 변경
delete(id_or_tag)                               객체를 캔버스에서 삭제합. 모두 지우려면 canvas.delete(&quot;all&quot;) 을 사용

주요 옵션
옵션                      허용 값                                    설명
tags        문자열 또는 문자열 튜플(예 : (&quot;tag1&quot;, &quot;tag2&quot;))             도형에 하나 이상의 태그를 부여하여, 동일 태그가 지정된 여러 도형을 그룹으로 묶어 한 번에 조작(이동, 색상 변경, 삭제 등)할 수 있게 함
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#ttk&amp;nbsp;위젯과&amp;nbsp;스타일링&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
ttk 소개와 기본 개념
tkinter 위젯과 ttk 위젯의 차이
외관이 OS 테마(Windows, macOS, Linux)에 자연스럽게 어울립니다.
색상&amp;middot;폰트 등을 위젯 옵션이 아닌 Style 객체로 관리합니다. 그래서 bg=, fg= 같은 옵션이 ttk 위젯
에는 없거나 무시되는 경우가 많습니다.
ttk는 Tk 8.5부터 도입되었습니다. Tk 버전은 다음과 같이 확인할 수 있습니다.
import tkinter
print(&quot;Tk Version:&quot;, tkinter.TkVersion)
print(&quot;Tcl Version:&quot;, tkinter.TclVersion)
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#ttk&amp;nbsp;기본&amp;nbsp;예제(Label,&amp;nbsp;Entry,&amp;nbsp;Button)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
label = ttk.Label(root, text=&quot;이름 : &quot;)
label.grid(row=0, column=0, padx=5, pady=5)
entry = ttk.Entry(root)
entry.grid(row=0, column=1, padx=5, pady=5)
button = ttk.Button(root, text=&quot;확인&quot;, command=lambda: print(entry.get()))
button.grid(row=1, column=0, columnspan=2, pady=5)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOUZOg/dJMcag7kFIC/123UYUbNAsPTLkdzPxeqi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOUZOg/dJMcag7kFIC/123UYUbNAsPTLkdzPxeqi1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOUZOg/dJMcag7kFIC/123UYUbNAsPTLkdzPxeqi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOUZOg%2FdJMcag7kFIC%2F123UYUbNAsPTLkdzPxeqi1%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;207&quot; height=&quot;98&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
스타일링과 테마

Style을 이용한 외관 제어

ttk의 핵심은 ttk.Style 객체입니다.위젯 클래스마다 스타일 이름(예 :TLabel )이 정해져 있으며, 
이를 통해 외관을 일괄 변경할 수 있습니다.
일반 tkinter 위젯은 위젯마다 font , bg 등의 옵션을 직접 지정하지만, ttk는 스타일을 한 번 
정의해두면 같은 스타일을 쓰는 모든 위젯에 일괄 적용됩니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk

root = tk.Tk()

style = ttk.Style()#스타일을 제어하는 객체를 생성
style.configure(&quot;TButton&quot;, font=(&quot;맑은 고딕&quot;, 11), padding=6)#기본 버튼 스타일인 TButton 을 수정
style.configure(&quot;Accent.TButton&quot;, foreground=&quot;lightgreen&quot;, background=&quot;green&quot;)#Accent.TButton이라는 사용자 정의 스타일 생성

btn1 = ttk.Button(root, text=&quot;기본 버튼&quot;)#기본 스타일 적용
btn1.pack(padx=50, pady=5)

btn2 = ttk.Button(root, text=&quot;강조 버튼&quot;, style=&quot;Accent.TButton&quot;)#사용자 정의 스타일을 적용
btn2.pack(padx=50, pady=5)

root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nKg8v/dJMcahLXcvh/CDkdzzRGjk9Rvsszz1uKsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nKg8v/dJMcahLXcvh/CDkdzzRGjk9Rvsszz1uKsk/img.png&quot; data-alt=&quot;ttk를 사용하면 이렇게 이쁜 버튼을 만들 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nKg8v/dJMcahLXcvh/CDkdzzRGjk9Rvsszz1uKsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnKg8v%2FdJMcahLXcvh%2FCDkdzzRGjk9Rvsszz1uKsk%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;210&quot; height=&quot;132&quot; data-origin-width=&quot;210&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ttk를 사용하면 이렇게 이쁜 버튼을 만들 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#테마&amp;nbsp;선택 &lt;br /&gt;#ttk는&amp;nbsp;테마를&amp;nbsp;제공하며&amp;nbsp;다음&amp;nbsp;코드로&amp;nbsp;사용&amp;nbsp;가능한&amp;nbsp;테마와&amp;nbsp;현재&amp;nbsp;사용중인&amp;nbsp;테마를&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
style = ttk.Style()
print(style.theme_names())
print(style.theme_use())
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LnFix/dJMcadpcUC6/czQG07kW4zowx22C1rBay1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LnFix/dJMcadpcUC6/czQG07kW4zowx22C1rBay1/img.png&quot; data-alt=&quot;결과:현재 테마의 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LnFix/dJMcadpcUC6/czQG07kW4zowx22C1rBay1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLnFix%2FdJMcadpcUC6%2FczQG07kW4zowx22C1rBay1%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;202&quot; height=&quot;232&quot; data-origin-width=&quot;202&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과:현재 테마의 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#테마를&amp;nbsp;변경하는&amp;nbsp;코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
style = ttk.Style()
frm = ttk.Frame(root, padding=20)
frm.pack()
cur = tk.StringVar(value=style.theme_use()) #콤보박스에 연결할 문자열 변수 정의
cb = ttk.Combobox(frm, values=style.theme_names(), textvariable=cur, state=&quot;readonly&quot;)#선택 가능한 테마 목록을 콤보박스로
cb.pack(pady=5)
cb.bind(&quot;&amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt;&quot;, lambda e: style.theme_use(cur.get()))#항목 선택 시 이벤트에 함수를 연결
ttk.Button(frm, text=&quot;버튼&quot;).pack(pady=5)
ttk.Checkbutton(frm, text=&quot;체크박스&quot;).pack(pady=5)
ttk.Progressbar(frm, value=60).pack(pady=5)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8k8TL/dJMcah6lwHv/5m8j7lO6au4LD5xAJfSJgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8k8TL/dJMcah6lwHv/5m8j7lO6au4LD5xAJfSJgK/img.png&quot; data-alt=&quot;테마형식을 선택할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8k8TL/dJMcah6lwHv/5m8j7lO6au4LD5xAJfSJgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8k8TL%2FdJMcah6lwHv%2F5m8j7lO6au4LD5xAJfSJgK%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;205&quot; height=&quot;201&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테마형식을 선택할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;203&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBnk28/dJMcac4WAu2/3ykCod4eBtGlYAGF6Uk8Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBnk28/dJMcac4WAu2/3ykCod4eBtGlYAGF6Uk8Bk/img.png&quot; data-alt=&quot;classic테마의 버튼, 체크박스, 프로그레스 바&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBnk28/dJMcac4WAu2/3ykCod4eBtGlYAGF6Uk8Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBnk28%2FdJMcac4WAu2%2F3ykCod4eBtGlYAGF6Uk8Bk%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;203&quot; height=&quot;218&quot; data-origin-width=&quot;203&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;classic테마의 버튼, 체크박스, 프로그레스 바&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#상태(state)와&amp;nbsp;동적&amp;nbsp;스타일 &lt;br /&gt;#ttk&amp;nbsp;위젯은&amp;nbsp;active&amp;nbsp;,&amp;nbsp;pressed&amp;nbsp;,&amp;nbsp;disabled&amp;nbsp;,&amp;nbsp;focus&amp;nbsp;등의&amp;nbsp;상태를&amp;nbsp;가지며,&amp;nbsp;style.map()&amp;nbsp;으로&amp;nbsp;상태별&amp;nbsp;외관을&amp;nbsp;지정할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
style = ttk.Style()
style.theme_use(&quot;clam&quot;)
style.map(&quot;TButton&quot;,
background=[(&quot;active&quot;, &quot;lightblue&quot;),
(&quot;pressed&quot;, &quot;blue&quot;),
(&quot;disabled&quot;, &quot;lightyellow&quot;)],
foreground=[(&quot;disabled&quot;, &quot;gray&quot;)])
ttk.Button(root, text=&quot;마우스를 올려보세요&quot;).pack(padx=20, pady=10)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;170&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JpNBK/dJMcacDYcLe/Azfhr2Mo3f431J3o7PqKPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JpNBK/dJMcacDYcLe/Azfhr2Mo3f431J3o7PqKPK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JpNBK/dJMcacDYcLe/Azfhr2Mo3f431J3o7PqKPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJpNBK%2FdJMcacDYcLe%2FAzfhr2Mo3f431J3o7PqKPK%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;170&quot; height=&quot;83&quot; data-origin-width=&quot;170&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#콤보박스&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
사용자가 드롭다운 목록에서 항목을 고르거나, 직접 값을 입력할 수 있습니다.
항목이 선택되면 &amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt; 가상 이벤트가 발생합니다.

콤보박스 사용하기

리스트로 넘겨받은 도시 이름을 콤보박스에서 사용해 선택된 값을 출력합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
combo = ttk.Combobox(root, values=[&quot;서울&quot;, &quot;부산&quot;, &quot;대구&quot;])
combo.set(&quot;서울&quot;)
combo.bind(&quot;&amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt;&quot;, lambda e: print(combo.get()))
combo.pack(padx=20, pady=20)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3BDKx/dJMcadQgJrA/gyG7p3kLSGcnBdvLkFF0a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3BDKx/dJMcadQgJrA/gyG7p3kLSGcnBdvLkFF0a1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3BDKx/dJMcadQgJrA/gyG7p3kLSGcnBdvLkFF0a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3BDKx%2FdJMcadQgJrA%2FgyG7p3kLSGcnBdvLkFF0a1%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;205&quot; height=&quot;93&quot; data-origin-width=&quot;205&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                             설명
get()                                           현재 입력되거나 선택된 값을 반환
set(value)                                      콤보박스의 값을 지정한 값으로 설정
current(index)                              인자가 없으면 선택된 항목의 인덱스를 반환하고, 인덱스를 주면 해당 항목을 선택
insert(index,value)                         입력 영역의 지정 위치에 문자열을 삽입
delete(first, last)                         입력 영역에서 지정 범위의 문자를 삭제
configure(options)                          위젯의 옵션을 동적으로 변경 (config 도 동일)
bind(event, func)                           특정 이벤트에 콜백 함수를 연결

주요 옵션
옵션                                       허용                                     값설명
values                        리스트 또는 튜플 (예: ['A', 'B','C'] )                드롭다운 목록에 표시할 항목
state                           normal , readonly , disabled                     입력 상태를 지정
textvariable                StringVar 등의 변수 객체                              값과 연동할 StringVar 등의 변수를 지정
width                       정수 (문자 단위)                                      위젯의 너비(문자 단위)를 지정
height                      정수 (항목 수)                                       드롭다운 목록에 한 번에 표시할 항목 수를지정
justify                  left , center , right                                  입력 텍스트의 정렬 방식을 지정
font                    폰트 지정 (예: ('맑은 고딕', 12) )                         표시 글꼴을 지정
postcommand             함수 또는 콜백 객체                                         드롭다운 목록이 열리기 직전에 호출할 함수를 지정

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
프로그레스바
작업의 진행 상황을 막대 형태로 시각화하는 위젯입니다.
진행률을 알 수 있을 때는 determinate 모드로 값을 채우고, 알 수 없을 때는 indeterminate
모드로 막대를 움직여 작업 중임을 표시합니다.
파일 다운로드, 설치, 데이터 처리 등 시간이 걸리는 작업의 상태를 사용자에게 알려줄 때 주로
사용합니다.
프로그레스바 사용하기
버튼을 클릭하면 Progressbar가 10%씩 증가합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
pb = ttk.Progressbar(root, length=200, mode=&quot;determinate&quot;, maximum=100)
pb.pack(padx=20, pady=20)
pb[&quot;value&quot;] = 0
def step():
    pb[&quot;value&quot;] = (pb[&quot;value&quot;] + 10) % 110

ttk.Button(root, text=&quot;+10&quot;, command=step).pack(pady=5)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QcknH/dJMcagM8PjO/QsktmCbUt0cGGwf6NWDSs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QcknH/dJMcagM8PjO/QsktmCbUt0cGGwf6NWDSs1/img.png&quot; data-alt=&quot;+10 버튼으로 프로그레스 바 조절 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QcknH/dJMcagM8PjO/QsktmCbUt0cGGwf6NWDSs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQcknH%2FdJMcagM8PjO%2FQsktmCbUt0cGGwf6NWDSs1%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;242&quot; height=&quot;129&quot; data-origin-width=&quot;242&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;+10 버튼으로 프로그레스 바 조절 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                         설명
start(interval=50)                      indeterminate 모드에서 애니메이션을 시작.interval 은 갱신 간격(ms) 
stop()                                  애니메이션 중지
step(amount=1.0)                        현재 값을 amount 만큼 증가 maximum 을 넘으면 0으로 순환
주요 옵션

옵션                                  허용 값                                    설명 
mode                            determinate , indeterminate                 값 기반 또는 애니메이션 모드를 지정
maximum                         실수 (기본값 100 )                           진행률의 최대값
value                           실수 (0 ~ maximum)                          현재 진행 값. determinate 모드에서 사용
variable                        IntVar 또는 DoubleVar 변수 객체               진행 값을 담을 변수를 연결 
length                          정수 (픽셀)                                  진행바의 길이
orient                          horizontal , vertical                       방향을 지정함

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
노트북 (탭)

여러 페이지를 탭으로 묶어, 클릭으로 전환할 수 있게 해주는 위젯입니다.
각 탭에는 보통 Frame을 연결하고, 그 안에 원하는 위젯을 배치합니다.
노트북 으로 탭 기능 사용하기
두 개의 프레임을 '메인 탭', '설정 탭'으로 구성합니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5ii6J/dJMcaixnqnB/t0l3ZygGzlkkMylkPhbnq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5ii6J/dJMcaixnqnB/t0l3ZygGzlkkMylkPhbnq0/img.png&quot; data-alt=&quot;결과. 메인,설정탭이 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5ii6J/dJMcaixnqnB/t0l3ZygGzlkkMylkPhbnq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5ii6J%2FdJMcaixnqnB%2Ft0l3ZygGzlkkMylkPhbnq0%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;302&quot; height=&quot;182&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과. 메인,설정탭이 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
주요 메서드
메서드                                                     설명
add(child, [options])                           새 탭을 추가. text로 탭 이름을 지정
insert(index, child, [options])                 지정한 위치( index )에 탭을 삽입
forget(tab_id)                                  해당 탭을 제거
select(tab_id)                                  지정한 탭을 선택(전환). 인자가 없으면 현재 선택된 탭을 반환
index(tab_id)                                   탭의 인덱스를 반환. &quot;end&quot;를 넘기면 탭 개수를 반환
tab(tab_id, **options)                          특정 탭의 옵션을 조회하거나 변경
주요 옵션
옵션                              허용                            값설명
text                            문자열                         탭에 표시할 이름 ( add / insert에서 사용)
state                    normal , disabled , hidden             탭 상태를 지정
image                    PhotoImage 등 이미지 객체                탭에 표시할 이미지
compound                 left , right , top , bottom ,center    텍스트와 이미지를 함께 배치할 때 위치를 지정
padding                  정수 또는 (좌, 상, 우, 하) 형식            탭 내부 여백
sticky                   n , s , e , w의 조합                   연결된 자식 위젯이 탭 영역에서 정렬&amp;middot;확장되는방식을 지정

'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#트리뷰&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
데이터를 행과 열로 표시하는 위젯으로, 표와 트리를 표현할 수 있습니다.
각 행은 항목(item)이며, 부모-자식 관계로 펼치고 접을 수 있는 트리 구조를 만들 수 있습니다.
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#트리뷰&amp;nbsp;사용하기&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
root = tk.Tk()
tree = ttk.Treeview(root, columns=(&quot;item&quot;, &quot;spec&quot;, &quot;price&quot;), show=&quot;headings&quot;)
tree.heading(&quot;item&quot;, text=&quot;제품&quot;)
tree.heading(&quot;spec&quot;, text=&quot;스펙&quot;)
tree.heading(&quot;price&quot;, text=&quot;가격&quot;)
data = [(&quot;가방&quot;, &quot;16인치&quot;, 123), (&quot;청소기&quot;, &quot;700W&quot;, 250), (&quot;운동화&quot;, &quot;275mm&quot;, 54)]
for item, spec, price in data:
    tree.insert(&quot;&quot;, &quot;end&quot;, values=(item, spec, price))

tree.pack(padx=5, pady=5, expand=True, fill=&quot;both&quot;)
root.mainloop()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erKxND/dJMcabLQObW/YXpdY8siy7P34QdOEgr6e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erKxND/dJMcabLQObW/YXpdY8siy7P34QdOEgr6e1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erKxND/dJMcabLQObW/YXpdY8siy7P34QdOEgr6e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FerKxND%2FdJMcabLQObW%2FYXpdY8siy7P34QdOEgr6e1%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;614&quot; height=&quot;268&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;'''
메서드                                             설명
insert(parent, index,**options)             항목을 추가. parent 가 &quot;&quot; 이면 최상위 항목. 추가된 항목 ID를 반환
delete(*items)                              지정한 항목을 삭제
heading(column, **options)                  열 머리글의 텍스트나 정렬 명령 등을 설정
column(column, **options)                   열의 너비, 정렬 방식 등을 설정
item(item, **options)                       특정 항목의 값이나 옵션을 조회&amp;middot;변경
selection()                                 현재 선택된 항목들의 ID를 반환
set(item, column, value)                    특정 항목의 셀 값을 조회하거나 변경
get_children(item=&quot;&quot;)                       해당 항목의 자식 항목 ID 목록을 반환

주요 옵션
옵션                                   허용 값                               설명
columns                             식별자 목록                          표시할 열들의 식별자 목록
show                                tree , headings , 또는 둘 다를조합     표시 영역을 지정
selectmode                          browse ,extended (기본), none         선택 방식(단일/다중/비활성)          
height                              정수                                  한 번에 보이는 행의 개수
text                                문자열                                 항목의 트리 열에 표시할 텍스트 (사용)                               
values                              값 목록                            항목의 각 열에 들어갈 값들 (insert 에서 사용)                              
anchor                              center ,w , e등                      열 내 텍스트 정렬 위치 (column 에서 사용)                    
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>데이터 분석</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/9</guid>
      <comments>https://bb0ng2.tistory.com/9#entry9comment</comments>
      <pubDate>Mon, 20 Jul 2026 21:34:36 +0900</pubDate>
    </item>
    <item>
      <title>0714 ML/DL(2)</title>
      <link>https://bb0ng2.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;머신러닝과 딥러닝에서의 선형대수학&lt;/b&gt;&lt;/i&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;ML과&amp;nbsp;DL에서&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;수&amp;nbsp;있도록&amp;nbsp;만들어주는&amp;nbsp;가장&amp;nbsp;핵심적인&amp;nbsp;수학적&amp;nbsp;기반. &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;ML/DL에서&amp;nbsp;선형대수가&amp;nbsp;필수적인&amp;nbsp;이유 &lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;데이터의&amp;nbsp;구조화&lt;/span&gt;:&amp;nbsp;현실의&amp;nbsp;데이터(이미지,&amp;nbsp;텍스트,&amp;nbsp;음성&amp;nbsp;등)를&amp;nbsp;컴퓨터가&amp;nbsp;다룰&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;숫자들의&amp;nbsp;배열(벡터,&amp;nbsp;행렬,&amp;nbsp;텐서)로&amp;nbsp;변환해&amp;nbsp;준다. &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;계산의&amp;nbsp;효율성&lt;/span&gt;:&amp;nbsp;수만&amp;nbsp;개의&amp;nbsp;수식을&amp;nbsp;하나하나&amp;nbsp;계산하는&amp;nbsp;대신,&amp;nbsp;행렬&amp;nbsp;연산을&amp;nbsp;통해&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;병렬로&amp;nbsp;처리&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;GPU가&amp;nbsp;딥러닝에&amp;nbsp;강력한&amp;nbsp;이유도&amp;nbsp;바로&amp;nbsp;이&amp;nbsp;행렬&amp;nbsp;연산에&amp;nbsp;최적화&amp;nbsp;되어&amp;nbsp;있기&amp;nbsp;때문. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;ML/DL에&amp;nbsp;적용되는&amp;nbsp;핵심&amp;nbsp;선형대수&amp;nbsp;개념 &lt;br /&gt;&lt;br /&gt;1)&amp;nbsp;벡터(Vector)와&amp;nbsp;행렬(Matrix),&amp;nbsp;텐서(Tensor)&amp;nbsp;데이터를&amp;nbsp;담는&amp;nbsp;그릇의&amp;nbsp;차원을&amp;nbsp;의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;벡터(&lt;span style=&quot;color: #ee2323;&quot;&gt;1차원&lt;/span&gt;):&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의&amp;nbsp;데이터&amp;nbsp;포인트. &lt;br /&gt;(예:&amp;nbsp;집의&amp;nbsp;평수,&amp;nbsp;방&amp;nbsp;개수,&amp;nbsp;가격을&amp;nbsp;담은&amp;nbsp;1차원&amp;nbsp;배열) &lt;br /&gt;행렬(&lt;span style=&quot;color: #ee2323;&quot;&gt;2차원&lt;/span&gt;):&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러&amp;nbsp;개의&amp;nbsp;데이터&amp;nbsp;포인트(행)와&amp;nbsp;각&amp;nbsp;데이터의&amp;nbsp;특징(열)을&amp;nbsp;담은&amp;nbsp;형태의&amp;nbsp;데이터&amp;nbsp;세트. &lt;br /&gt;텐서(&lt;span style=&quot;color: #ee2323;&quot;&gt;3차원&amp;nbsp;이상&lt;/span&gt;):&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬러&amp;nbsp;이미지(가로,&amp;nbsp;세로,&amp;nbsp;RGB&amp;nbsp;채널)나&amp;nbsp;비디오&amp;nbsp;데이터&amp;nbsp;등을&amp;nbsp;표현할&amp;nbsp;때&amp;nbsp;사용. &lt;br /&gt;딥러닝&amp;nbsp;프레임워크인&amp;nbsp;'TensorFlow(텐서플로우)'의&amp;nbsp;이름도&amp;nbsp;여기서&amp;nbsp;유래. &lt;br /&gt;&lt;br /&gt;2)&amp;nbsp;행렬&amp;nbsp;곱&amp;nbsp;(Matrix&amp;nbsp;Multiplication)&amp;nbsp;:&amp;nbsp;딥러닝의&amp;nbsp;인공신경망(Neural&amp;nbsp;Network)&amp;nbsp;연산의&amp;nbsp;90%&amp;nbsp;이상은&amp;nbsp;행렬&amp;nbsp;곱셈.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3)&amp;nbsp;노름&amp;nbsp;(Norm) &lt;br /&gt;&lt;br /&gt;벡터의&amp;nbsp;크기나&amp;nbsp;길이를&amp;nbsp;측정하는&amp;nbsp;방법. &lt;br /&gt;ML/DL에서는&amp;nbsp;모델이&amp;nbsp;학습데이터에만&amp;nbsp;너무&amp;nbsp;과도하게&amp;nbsp;맞춰지는&amp;nbsp;'과적합(Overfitting)을&amp;nbsp;방지하는&amp;nbsp;규제로&amp;nbsp;주로&amp;nbsp;쓰인다. &lt;br /&gt;&lt;br /&gt;4)&amp;nbsp;고윳값과&amp;nbsp;고유벡터 &lt;br /&gt;행렬을&amp;nbsp;곱해도&amp;nbsp;방향이&amp;nbsp;변하지&amp;nbsp;않고&amp;nbsp;크기만&amp;nbsp;변하는&amp;nbsp;벡터를&amp;nbsp;고유벡터,&amp;nbsp;그때&amp;nbsp;변화하는&amp;nbsp;크기를&amp;nbsp;고윳값이라고&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;차원&amp;nbsp;축소(PCA):&amp;nbsp;데이터의&amp;nbsp;특성이&amp;nbsp;너무&amp;nbsp;많을&amp;nbsp;때,&amp;nbsp;정보&amp;nbsp;손실을&amp;nbsp;최소화&amp;nbsp;하면서&amp;nbsp;차원을&amp;nbsp;줄이는&amp;nbsp;주성분(PCA)&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;br /&gt;선형대수의&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;벡터의&amp;nbsp;내적과&amp;nbsp;행렬의&amp;nbsp;곱셈,&amp;nbsp;그리고&amp;nbsp;몇&amp;nbsp;가지&amp;nbsp;필수&amp;nbsp;연산&amp;nbsp;규칙 &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;벡터의&amp;nbsp;내적 &lt;br /&gt;#&amp;nbsp;함수를&amp;nbsp;만들어&amp;nbsp;벡터의&amp;nbsp;내적&amp;nbsp;구하기&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;def dot(v,w):
    return sum(v_i*w_i for v_i, w_i in zip(v,w))

A=[4,3]
B=[6,0]

dot(A,B)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 내적 함수를 만드는 방식. v,w의 요소끼리 곱한 값들을 모두 더해 반환한다. (출력결과 : 24)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;넘파이로&amp;nbsp;벡터의&amp;nbsp;내적&amp;nbsp;구하기&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;a=np.array([1,2,3,4,5])
b=np.array([2,4,6,8,10])
print(a*b)
print(sum(a*b))
print(np.sum(a*b))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넘파이를 딸깍하면 요소의 개수만 맞춰주면 a*b만으로도 각 요소의 곱 array를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sum함수만 취해주면내적 완성!&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;브로드캐스팅으로&amp;nbsp;벡터의&amp;nbsp;내적&amp;nbsp;구하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;np_Hong1 = np.array([80,90,10,9])
np_Jung1 = np.array([70,80,9,8])
v=np_Hong1+np_Jung1
w=[0.5,0.5,0.5,0.5]
[i*j for i,j in zip(v,w)]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교를 위해 위 코드는 넘파이 어레이로 구한 홍원과 정원의평균이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;#넘파이 브로드캐스팅 실습
######벡터와 스칼라는 관례적으로 1-벡터를 사용하여 스칼라를 벡터로 변환하는 연산을 허용하는데, 이를 브로드캐스팅이라고 부른다!!

avg=[75.0,85.0,9.5,8.5]
avg1=np.array(avg)
W1=np.array([0.4,0.4,1,1])
print(avg1*W1)
#내적
Final=avg1*W1
print(Final.sum())&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드가 바로 브로드캐스팅을 이용한 내적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가중치 array W1과, 정원이와 홍원이의 평균점수 array avg1을 요소끼리 곱한 Final arry의 합계를 구하면 내적을 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과: 82.0&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;br /&gt;#넘파이&amp;nbsp;함수로&amp;nbsp;벡터의&amp;nbsp;길이&amp;nbsp;구하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a1 = np.array([1,2])
np.linalg.norm(a1) # 넘파이의 벡터의 길이를 구하는 함수&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 평면에서 벡터 (1,2)의 길이는&amp;nbsp;루트(1^2+2^2) 인데, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;넘파이의 linalg.norm 함수를 사용하면 벡터의 길이를 그냥 딸~깍 해서 구할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;출력 결과: np.float64(2.23606797749979)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 넘파이 없이 파이썬 함수만으로 구현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;def dot(v,w):
    return sum(v_i*w_i for v_i, w_i in zip(v,w))

def sum_of_squares(v):
    return dot(v,v)
v=[1,2]

import math # 수학의 함수들을 사용할떄
def magnitude(v):
    return math.sqrt(sum_of_squares(v))
v=[1,2]
mag=magnitude(v)
print(mag)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어우~복잡해. 넘파이를 쓰도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과: 2.23606797749979&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;간의&amp;nbsp;거리(유클리드&amp;nbsp;거리)&amp;nbsp;구하기 &lt;br /&gt;#유클리드&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;br /&gt;#math&amp;nbsp;라이브러리의&amp;nbsp;sqrt함수를&amp;nbsp;사용하여&amp;nbsp;계산&amp;nbsp;할&amp;nbsp;수도&amp;nbsp;있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;def vector_subtract(v,w):
    return [v_i-w_i for v_i, w_i in zip(v,w)]
def squared_distance(v,w):
    return sum_of_squares(vector_subtract(v,w))

v=[1,2]
w=[3,4]
sd=squared_distance(v,w)

def distance(v,w):
    return math.sqrt(squared_distance(v,w))

def distance1(v,w):
    return magnitude(vector_subtract(v,w))
v=[1,2]
w=[3,4]

print(distance(v,w))
print(distance1(v,w))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유클리드 거리라 함은 공간에서의 두 벡터의 시작점을 원점으로 통일 시키고 벡터 끝이 가리키는점 사이의 거리라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드의 distance함수는 넘파이 없이 파이썬만으로 유클리드 거리를 구한 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vector_subtract 함수는 리스트의 각 요소의 차를 저장한 리스트를 반환, squared_distance함수는 v,w의 요소의 차를 제곱한 값을 담은 리스트를 반환한다.distance 함수는 math라이브러리의 sqrt(제곱근 함수)와 squared_distance함수를 사용하여 유클리드 거리를구하는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;distance1함수는&amp;nbsp; magnitude함수와 vector_subtract함수를 사용해서 유클리드 거리를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.8284271247461903 &lt;br /&gt;2.8284271247461903&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp; 벡터를 이용한 평균 제곱 오차와 평균 제곱근 오차&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;### 평균 제곱 오차
#예측값이 실제값에 얼마나 근접한지 나타냄
y=[1,1,0,1,0,1,0,0]#실제값
t=[0,0,1,0,0,1,0,0]#예측값
import numpy as np

def mean_squared_error(y,t):
    return 0.5*np.sum((y-t)**2)

print(mean_squared_error(np.array(y),np.array(t)))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;출력 결과: 2.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균인데 왜 0.5를 곱할까? (y-t)**2의 어레이의 크기는 8인데?? 라는 궁금증에 대한 클로드의 답변:&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;딥러닝(신경망) 학습에서 자주 쓰이는 손실 함수(loss function) 공식&lt;/b&gt;이에요. 유명한 딥러닝 교재(사이토 고키의 &quot;밑바닥부터 시작하는 딥러닝&quot; 등)에서 이 형태를 &quot;평균제곱오차&quot;라는 이름으로 소개하는 경우가 있는데, 엄밀히 통계학적 정의의 &quot;평균&quot;과는 다른 형태예요. &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;제곱근&amp;nbsp;오차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;c=np.array([1,2,3,4,5])
d=np.array([2,4,6,8,10])
print((sum((c-d)**2)/len(c))**(1/2))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;b&gt;행렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import numpy as np
A=[[1,2,3],
   [4,5,6]]
B= [[1,2],
    [3,4],
    [5,6]]
A1=np.array(A)
A1.shape
np.shape(A)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A와 B는 각각 2행3열, 3행 2열의 리스트. A1은A를 행렬로 만든것.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A1.shape &lt;br /&gt;&amp;gt;&amp;gt;(2, 3) &lt;br /&gt;np.shape(A) &lt;br /&gt;&amp;gt;&amp;gt;(2, 3)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#행렬의 덧셈, 뺄셈, 곱셈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;x=np.array([10,11,12,13,14])
y=np.array([0,1,2,3,4])
x+y
x-y
#행렬의 곱셈
x= np.array([[1],[2],[3]])
y= np.array([[4],[5],[6]])
x*y&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;x.shape, y.shape # (3, 1), (3,1)
x.T
x.T.shape
x.T*y&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x,y는 3행 1열 인데, x에 전치를 함으로써 x는 1행 3열,&amp;nbsp; y는 3행 1열.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 곱해주면 형태가 안맞기 때문에 브로드캐스팅이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 2 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 2 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 2 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4 4 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 5 5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6 6 6&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;array([[&amp;nbsp;4,&amp;nbsp;&amp;nbsp;8,&amp;nbsp;12], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;5,&amp;nbsp;10,&amp;nbsp;15], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;6,&amp;nbsp;12,&amp;nbsp;18]])&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#행렬곱&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;x=np.array([[1,2,3]])
y=np.array([[4,5,6]])
print(x.T@y)
print(x@y.T)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬곱을 하려면 @연산기호를 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 어레이에 전치를 해서 행렬곱을 해주면 결과는 3x3, 1x1이 나오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[[&amp;nbsp;4&amp;nbsp;&amp;nbsp;5&amp;nbsp;&amp;nbsp;6] &lt;br /&gt;&amp;nbsp;[&amp;nbsp;8&amp;nbsp;10&amp;nbsp;12] &lt;br /&gt;&amp;nbsp;[12&amp;nbsp;15&amp;nbsp;18]] &lt;br /&gt;[[32]]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;손실함수와 활성화함수&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
손실 함수 (Loss Function)
MSE : 모델읠 출력층에서 선형회귀, 주택 가격 등 수치 예측
MAE : 모델의 출력층에서 이상치가 많은 환경에서의 수치 예측
BCE : 분류류기의 최종 출력층, 주요모델은 불량 탐지, GAN의 판별자
CCE : 다중 분류기의 최종출력층, 이미지분류, 텍스트 카테고리 분류

회귀 : MSE
이진 : BCE
다중 : CCE

활성화 함수(Activation Function)
이전 층으로 부터 전달받은
Sigmoid : 이진분류 모델의 최종 출력층
Tanh : RNN의 은닉층에서
ReLU : 대부분의 은닉층에서
Softmax : 다중 분류의 최종 출력층에서
은닉층 : ReLU
출력층 : Softmax,Sigmoid
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;# 손실 함수의 예
import tensorflow as tf&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;MSE&amp;nbsp;(Mean&amp;nbsp;Squared&amp;nbsp;Error)&amp;nbsp;실례&amp;nbsp;-&amp;nbsp;수치&amp;nbsp;예측&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;mse_loss_fn = tf.keras.losses.MeanSquaredError()
# 예: 모델의 집값 예측이 10.0, 실제 정답(집값)이 12.0인 경우
predictions = tf.constant([[10.0]]) # 예측값
targets = tf.constant([[12.0]])     # 정답
# TensorFlow에서는 관례상 (정답, 예측값) 순서로 입력합니다.
loss_mse = mse_loss_fn(targets, predictions)
print(f&quot;MSE Loss: {loss_mse.numpy()}&quot;) # 결과: 4.0 ((12-10)^2)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;2.&amp;nbsp;BCE&amp;nbsp;(Binary&amp;nbsp;Cross&amp;nbsp;Entropy)&amp;nbsp;실례&amp;nbsp;-&amp;nbsp;이진&amp;nbsp;분류&amp;nbsp;(합격/불합격)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;bce_loss_fn = tf.keras.losses.BinaryCrossentropy() #- 위치: 다중
#분류기의 최종
#출력층 - 주요 모델: 이미지
#분류, 텍스트
#카테고리 분류
# 예: 모델이 '합격(1)'일 확률을 0.8로 예측했고, 실제 정답도 '합격(1.0)'인 경우
pred_probs = tf.constant([[0.8]])
target_binary = tf.constant([[1.0]])
loss_bce = bce_loss_fn(target_binary, pred_probs)
print(f&quot;BCE Loss: {loss_bce.numpy()}&quot;) # 예측이 맞았으므로 매우 낮은 손실값 출력&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;3.&amp;nbsp;CCE&amp;nbsp;(Categorical&amp;nbsp;Cross&amp;nbsp;Entropy)&amp;nbsp;실례&amp;nbsp;-&amp;nbsp;다중&amp;nbsp;분류&amp;nbsp;(개/고양이/새)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;cce_loss_fn = tf.keras.losses.CategoricalCrossentropy()
# 정답이 원-핫 인코딩 되어있다고 가정 (고양이: [0, 1, 0])
target_class = tf.constant([[0.0, 1.0, 0.0]])
# 모델이 예측한 각 클래스별 속할 확률 (합이 1.0)
pred_probs_multi = tf.constant([[0.1, 0.8, 0.1]])
loss_cce = cce_loss_fn(target_class, pred_probs_multi)
print(f&quot;CCE Loss: {loss_cce.numpy()}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#활성함수의 예
import tensorflow as tf
# 임의의 1차원 데이터 생성
input_data = tf.constant([-2.0, 0.0, 2.0])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;1.&amp;nbsp;Sigmoid&amp;nbsp;실례&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;out_sigmoid = tf.keras.activations.sigmoid(input_data)
print(f&quot;Sigmoid Out: {out_sigmoid.numpy()}&quot;)
# 결과: 음수는 0에 가깝게, 양수는 1에 가깝게 변환됨 (0과 1 사이)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 자세히 말하면 Sigmoid(x)는 1/e^x+1이다. 자연상수 e의 지수함수를 사용함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x가 무한히 커지면 Sigmoid(x)는 0에 수렴,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x가 무한히 작아지면 Sigmoid(x)는 1에 수렴.&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;2.&amp;nbsp;ReLU&amp;nbsp;실례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;out_relu = tf.keras.activations.relu(input_data)
print(f&quot;ReLU Out: {out_relu.numpy()}&quot;)
# 결과: [0.0, 0.0, 2.0] -&amp;gt; 0 미만의 값은 모두 0으로 깎아버림&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;3.&amp;nbsp;Leaky&amp;nbsp;ReLU&amp;nbsp;실례&amp;nbsp;(tf.nn&amp;nbsp;모듈&amp;nbsp;사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;out_leaky = tf.nn.leaky_relu(input_data, alpha=0.1) # 음수일 때 곱할 작은 기울기(alpha) 설정
print(f&quot;Leaky ReLU Out: {out_leaky.numpy()}&quot;)
# 결과: [-0.2, 0.0, 2.0] -&amp;gt; 음수값이 완전히 0이 되지 않고 약간의 값을 유지 (Dying ReLU 방지)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;4.&amp;nbsp;Softmax&amp;nbsp;실례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;# 텐서플로우의 Softmax는 다차원 텐서를 기대하므로 2차원 형태로 만들어 계산
input_data_2d = tf.constant([[-2.0, 0.0, 2.0]])
out_softmax = tf.keras.activations.softmax(input_data_2d)
print(f&quot;Softmax Out: {out_softmax.numpy()}&quot;)
# 결과: 가장 큰 값인 2.0이 가장 높은 확률을 가지며, 값들의 합이 무조건 1.0이 됨
print(f&quot;Softmax Sum: {tf.reduce_sum(out_softmax).numpy()}&quot;) # 합: 1.0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 Softmax와 sigmoid가 가장 많이 쓰인다고 함ㅇㅇ&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;&lt;i&gt;&lt;b&gt;순전파와 역전파&lt;/b&gt;&lt;/i&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;br /&gt;#지방과&amp;nbsp;소금&amp;nbsp;성분에&amp;nbsp;따른&amp;nbsp;치즈&amp;nbsp;선호도 &lt;br /&gt;#&amp;nbsp;독립변수&amp;nbsp;:&amp;nbsp;Fat,&amp;nbsp;Salt &lt;br /&gt;# 종속변수 : Acceptance (like, dislike)&lt;br /&gt;#&amp;nbsp;3개&amp;nbsp;계층으로&amp;nbsp;이루어지며&amp;nbsp;왼쪽부터&amp;nbsp;입력층,&amp;nbsp;은닉층,&amp;nbsp;출력층&amp;nbsp;구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#시그모이드&amp;nbsp;함수&amp;nbsp;시각화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
X=np.arange(-5.0, 5.0, 0.1)
Y=sigmoid(X)
plt.plot(X, Y)
plt.ylim(-0.1, 1.1)
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맷플롯립을 사용해서 시그모이드 함수를 시각화하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HpYwJ/dJMcaiD32A3/dqN1Cz7MqQNmhl41jsByW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HpYwJ/dJMcaiD32A3/dqN1Cz7MqQNmhl41jsByW1/img.png&quot; data-alt=&quot;시그모이드 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HpYwJ/dJMcaiD32A3/dqN1Cz7MqQNmhl41jsByW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHpYwJ%2FdJMcaiD32A3%2FdqN1Cz7MqQNmhl41jsByW1%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;642&quot; height=&quot;562&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시그모이드 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그모이드 함수는 '기울기 소실'이라는 한계점이 있기 때문에 이진분류의 출력층에만 제한적으로 주로 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그모이드 함수의 그래프를 보면, 양 끝 곡선이 거의 평평한데, 이때 기울기는 거의 0에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사하강법에 따르면, 모델 학습은 기울기를 따라 가중치를 조금씩 업데이트 하는것인데, 기울기가 0에 가깝기 때문에 가중치가 거의 업데이트 되지 않아서, 학습이 멈춘것 처럼 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런한계 때문에, 오늘날신경망의 은닉층에서는 시그모이드 대신 ReLU같은 함수를 훨씬 많이 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 2*3*1 구조의 간단한 순전파 신경망 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;input=[0.2, 0.9] # 입력층의 2개의 값
weight1=[[0.05, 0.01], [-0.01, 0.03], [0.02, -0.01]] # 입력층에서 은닉층으로 가는 가중치
bias1=[-0.3, 0.2, 0.05] # 은닉층으로 가는 바이어스
weight2=[0.01,0.05,0.015] # 은닉층에서 출력층으로 가는 가중치
bias2=[-0.015] # 출력층으로 가는 바이어스&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 입력층의 2개의 값(지방과 소금의 양)과, 입력층에서 은닉층으로 가는 가중치 weight1, 은닉층으로 가는 바이어스 bias1, 은닉층에서 출력층으로 가는 가중치 weight2, 출력층으로 가는 바이어스 bias2를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값을 제외한 가중치와 편향은 임의로 정해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;입력층의&amp;nbsp;첫&amp;nbsp;번째&amp;nbsp;노드에서&amp;nbsp;은닉층의&amp;nbsp;첫&amp;nbsp;번째&amp;nbsp;노드로&amp;nbsp;신호&amp;nbsp;전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;def dot(v, w):
    return sum(i * j for i, j in zip(v, w))
dot(input,weight1[0])+bias1[0]  #0.2*0.05 +0.9*0.01 + (-0.3) # =-0.28099999999999997&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input 벡터 (0.2,0.9) 와 weight1[0] 벡터 (0.05,0.01)을 내적하고 bias1[0]을 더해서 A3을 구한다.&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;위와 같은 방식으로, A3,A4,A5를 구한다.&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;입력층에서&amp;nbsp;은닉층&amp;nbsp;노드로&amp;nbsp;보내는&amp;nbsp;신호값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;A3=dot(input,weight1[0])+bias1[0]
A4=dot(input,weight1[1])+bias1[1]
A5=dot(input,weight1[2])+bias1[2]
A3, A4, A5 #(-0.28099999999999997, 0.225, 0.045)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇㅇ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#시그모이드&amp;nbsp;함수를&amp;nbsp;이용해&amp;nbsp;A3,&amp;nbsp;A4,&amp;nbsp;A5&amp;nbsp;노드에서&amp;nbsp;출력하는&amp;nbsp;값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import numpy as np
def sigmoid(x):
    return 1 / (1 + np.exp(-x)) # np.exp(x)는 자연상수 e의 x 승 값을 리턴함

outputA3=sigmoid(A3)
outputA4=sigmoid(A4)
outputA5=sigmoid(A5)
outputA3, outputA4, outputA5 #(0.4302086298074603, 0.5560138905446199, 0.5112481019468548)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#은닉층에서&amp;nbsp;출력층으로&amp;nbsp;신호&amp;nbsp;전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;Y1=[outputA3, outputA4, outputA5]
A6=dot(Y1, weight2)+bias2
outputA6=sigmoid(A6)
outputA6 #array([0.50619256])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;2*3*1 구조의 간단한 순전파 신경망을 구현해보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이제 넘파이를 이용해서 계산해보자.&lt;/span&gt;&lt;/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;넘파이를&amp;nbsp;활용한&amp;nbsp;순전파&amp;nbsp;신경망&amp;nbsp;계산&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
지금까지의 계산을 넘파이를 이용하면 매우 편리하게 진행할 수 있다. 입력층에서 은닉층으로 전달되는 값 
A1을 구하고 시그모이드 함수를 이용하여 출력층으로 전달하기 위한 값 Y1을 구한다.
'''
# 입력층에서 첫 번째 은닉층으로 신호 전달 방법
import numpy as np
input=np.array([0.2, 0.9]) # 1*2 행렬
weight1=np.array([[0.05,0.01], [-0.01,0.03], [0.02,-0.01]]) # 3*2 행렬
bias1=np.array([-0.3,0.2,0.05])
A1=np.dot(input, weight1.T) + bias1 # weight1.T는 weight의 전치 행렬
print(A1)
Y1=sigmoid(A1)
Y1 #array([0.43020863, 0.55601389, 0.5112481])

'''
다음은 은닉층의 값 Y1에 가중치와 바이어스 값을 적용해 출력층으로 전달하는 과정이다. 이 값은 A2에 
저장하고 시그모이드 함수를 이용해 출력층에서 최종 산출하는 값을 Y2에 저장한다.
'''
weight2=np.array([0.01, 0.05, 0.015])
bias2=np.array([-0.015])
A2=np.dot(Y1, weight2.T)+bias2
Y2=sigmoid(A2)
Y2 #array([0.50619256])

'''
보통 마지막 출력층에서는 시그모이드 함수 대신 항등 함수를 사용하지만 이 책에서는 편의상 최종 출력
값으로 시그모이드 함수를 사용한 값을 이용하기로 한다. 항등 함수를 사용할 때 출력층의 값은 다음과 같
다. 
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넘파이를 사용하면 행렬곱 연산이 용이해서 훨씬 쉽게 구현 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#항등함수를 이용해서 출력층 구하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def identity_function(x):
    return x
Y=identity_function(A2) # 혹은 Y=A2
Y #array([0.0247715])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def init_network():
  network={}
  network['W1']=np.array([[0.05,0.01], [-0.01,0.03], [0.02,-0.01]])
  network['b1']=np.array([-0.3,0.2,0.05])
  network['W2']=np.array([0.01, 0.05, 0.015])
  network['b2']=np.array([-0.015])
         return network
def forward(network, x):
  W1, W2=network['W1'], network['W2']
  b1, b2=network['b1'], network['b2']
  a1=np.dot(x, W1.T) + b1
  y1=sigmoid(a1)
  a2=np.dot(y1, W2.T) + b2
  y2=sigmoid(a2) # 시그모이드 함수 사용
  # z2=identity_function(a2) # 항등 함수 사용
        return y2

network=init_network()
input=np.array([0.2, 0.9])
Y=forward(network, input)
Y #array([0.50619256])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/7</guid>
      <comments>https://bb0ng2.tistory.com/7#entry7comment</comments>
      <pubDate>Tue, 14 Jul 2026 23:13:09 +0900</pubDate>
    </item>
    <item>
      <title>0713  ML/DL</title>
      <link>https://bb0ng2.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ML/DL: 머신러닝과 딥러닝은 컴퓨터가 데이터를 통해 스스로 규칙을 학습하게 만드는 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;AI (인공지능) &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; └─ ML (머신러닝) &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;└─ DL (딥러닝)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝/딥러닝 결과가 컴퓨터마다 다른 이유는 알고리즘 본연의 무작위성, 하드웨어 연산 방식의 차이, 소프트웨어 및 라이브러리 버전 차이 등이 있다. 딥러닝에서 하드웨어 차이로 인해 발생하는 1% 미만의 성능 차이는 &lt;b&gt;통제할 수 없는 자연스러운 현상&lt;/b&gt;으로 받아들이고, 오차에 집착하기 보다는 전반적인 모델의 성능을 향상시키는데 집중하는 것이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;ML이&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;것&amp;nbsp;(잘하는&amp;nbsp;것)&lt;/i&gt;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;정형&amp;nbsp;데이터&amp;nbsp;분석&amp;nbsp;및&amp;nbsp;예측&lt;/b&gt;:&amp;nbsp;엑셀이나&amp;nbsp;데이터베이스에&amp;nbsp;깔끔하게&amp;nbsp;정리된&amp;nbsp;숫자,&amp;nbsp;텍스트&amp;nbsp;데이터를&amp;nbsp;분석 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;분류&amp;nbsp;및&amp;nbsp;군집화&lt;/b&gt;:&amp;nbsp;스팸&amp;nbsp;메일을&amp;nbsp;걸러내거나,&amp;nbsp;고객을&amp;nbsp;구매&amp;nbsp;성향에&amp;nbsp;따라&amp;nbsp;몇&amp;nbsp;가지&amp;nbsp;그룹으로&amp;nbsp;나누는&amp;nbsp;작업 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;명확한&amp;nbsp;추천&amp;nbsp;시스템&lt;/b&gt;:&amp;nbsp;사용자의&amp;nbsp;과거&amp;nbsp;클릭&amp;nbsp;이력,&amp;nbsp;조회수,&amp;nbsp;평점&amp;nbsp;등을&amp;nbsp;바탕으로&amp;nbsp;좋아할&amp;nbsp;만한&amp;nbsp;콘텐츠 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;해석&amp;nbsp;가능성&lt;/b&gt;:&amp;nbsp;AI가&amp;nbsp;왜&amp;nbsp;이런&amp;nbsp;결과를&amp;nbsp;도출했는지&amp;nbsp;사람이&amp;nbsp;역으로&amp;nbsp;추적하고&amp;nbsp;이해 &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;ML이&amp;nbsp;할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;것&amp;nbsp;(한계점)&lt;/i&gt;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;비정형&amp;nbsp;데이터의&amp;nbsp;독립적&amp;nbsp;이해:&amp;nbsp;이미지,&amp;nbsp;음성,&amp;nbsp;영상,&amp;nbsp;긴&amp;nbsp;줄글&amp;nbsp;형태의&amp;nbsp;텍스트&amp;nbsp;자체를&amp;nbsp;그냥&amp;nbsp;던져주면&amp;nbsp;스스로&amp;nbsp;이해하지&amp;nbsp;못한다. &lt;br /&gt;&lt;br /&gt;고차원적인&amp;nbsp;창작:&amp;nbsp;새로운&amp;nbsp;글을&amp;nbsp;지어내거나,&amp;nbsp;완전히&amp;nbsp;새로운&amp;nbsp;이미지를&amp;nbsp;그려내는&amp;nbsp;등의&amp;nbsp;복잡한&amp;nbsp;생성&amp;nbsp;작업은&amp;nbsp;하지&amp;nbsp;못한다. &lt;br /&gt;&lt;br /&gt;딥러닝(Deep&amp;nbsp;Learning,&amp;nbsp;DL):&amp;nbsp;머신러닝의&amp;nbsp;한&amp;nbsp;분야. &lt;br /&gt;인간의&amp;nbsp;뇌&amp;nbsp;신경망을&amp;nbsp;모방한&amp;nbsp;인공신경망을&amp;nbsp;층층이&amp;nbsp;깊게&amp;nbsp;쌓아(Deep),&amp;nbsp;기계가&amp;nbsp;스스로&amp;nbsp;데이터의&amp;nbsp;특징을&amp;nbsp;찾아내도록&amp;nbsp;하는&amp;nbsp;방식 &lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;DL이&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;것&amp;nbsp;(잘하는&amp;nbsp;것)&lt;/span&gt;&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;비정형&amp;nbsp;데이터&amp;nbsp;처리의&amp;nbsp;최강자:&amp;nbsp;사진&amp;nbsp;속&amp;nbsp;객체&amp;nbsp;인식,&amp;nbsp;음성&amp;nbsp;인식(STT),&amp;nbsp;음성&amp;nbsp;합성(TTS),&amp;nbsp;자연어&amp;nbsp;이해&amp;nbsp;등&amp;nbsp;복잡하고&amp;nbsp;규칙이&amp;nbsp;없는&amp;nbsp;데이터를&amp;nbsp;처리하는&amp;nbsp;데&amp;nbsp;압도적인&amp;nbsp;성능 &lt;br /&gt;&lt;br /&gt;고도의&amp;nbsp;생성형&amp;nbsp;AI:&amp;nbsp;챗GPT처럼&amp;nbsp;자연스러운&amp;nbsp;문장을&amp;nbsp;이어나가거나,&amp;nbsp;글의&amp;nbsp;문맥을&amp;nbsp;파악해&amp;nbsp;요약하고,&amp;nbsp;텍스트를&amp;nbsp;기반으로&amp;nbsp;새로운&amp;nbsp;이미지를&amp;nbsp;만들어내는&amp;nbsp;등&amp;nbsp;창작의&amp;nbsp;영역에&amp;nbsp;가까운&amp;nbsp;결과 &lt;br /&gt;&lt;br /&gt;스스로&amp;nbsp;특징&amp;nbsp;추출:&amp;nbsp;사람이&amp;nbsp;특징을&amp;nbsp;지정해주지&amp;nbsp;않아도,&amp;nbsp;방대한&amp;nbsp;데이터를&amp;nbsp;보면서&amp;nbsp;스스로&amp;nbsp;&quot;아&amp;nbsp;고양이는&amp;nbsp;귀가&amp;nbsp;뾰족하고&amp;nbsp;수염이&amp;nbsp;있구나&quot;라는&amp;nbsp;패턴 &lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;DL이&amp;nbsp;할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;것&amp;nbsp;(한계점)&lt;/span&gt;&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;적은&amp;nbsp;데이터로&amp;nbsp;학습하기&amp;nbsp;:&amp;nbsp;데이터가&amp;nbsp;적으면&amp;nbsp;아예&amp;nbsp;제대로&amp;nbsp;된&amp;nbsp;학습&amp;nbsp;결과를&amp;nbsp;내지&amp;nbsp;못한다. &lt;br /&gt;&lt;br /&gt;과정&amp;nbsp;설명하기:&amp;nbsp;&quot;왜&amp;nbsp;이런&amp;nbsp;결과가&amp;nbsp;나왔어?&quot;라고&amp;nbsp;물었을&amp;nbsp;때&amp;nbsp;그&amp;nbsp;이유를&amp;nbsp;설명하지&amp;nbsp;못한다. &lt;br /&gt;&lt;br /&gt;상식적&amp;nbsp;추론&amp;nbsp;:&amp;nbsp;문맥상&amp;nbsp;그럴듯한(확률이&amp;nbsp;높은)&amp;nbsp;단어를&amp;nbsp;나열&amp;nbsp;할&amp;nbsp;뿐,&amp;nbsp;인간이&amp;nbsp;가진&amp;nbsp;기본적인&amp;nbsp;'상식'이나&amp;nbsp;세계관을&amp;nbsp;이해&amp;nbsp;하고&amp;nbsp;논리적으로&amp;nbsp;사유하는게&amp;nbsp;아니다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 이론을 프로그래밍 코드로 작성하는 것을 돕는 라이브러리는 텐서플로, 파이토치, 카페, 테아노 등이 있다. 그 중 우리가 사용할것은 바로 &lt;i&gt;&lt;b&gt;텐서플로&lt;/b&gt;&lt;/i&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #191a1c; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;'''
Tensor Flow

Layer [Input]
Layer [Dense]
위와 같은 레이어가 쌓여있는 구조.

독립  종속
-----------
온도  판매량
.       .
.       .
.       .
.       .
----------
인풋을 넣었을떄 덴스가 정답과 오차가 있을텐데, 이를 조정하는게 loss 함수
mse(평균제곱오차)를 사용해서 오차를 조절(가중치 조절)

이미지분석 : 보통 2가지가 있음
1. 객체탐지 (ex-Yolo 모델)
2. 세그먼트 탐지

손실함수와 활성함수를 어떨때 뭘 쓰는지를 잘 판단해야함!!
'''&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;지도학습&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;#&amp;nbsp;1&amp;nbsp;과거의&amp;nbsp;데이터를&amp;nbsp;준비한다.&amp;nbsp;(기온과&amp;nbsp;레모네이드&amp;nbsp;판매량,&amp;nbsp;원인&amp;nbsp;:&amp;nbsp;독립변수,&amp;nbsp;결과:&amp;nbsp;종속변수) &lt;br /&gt;#&amp;nbsp;2&amp;nbsp;모델의&amp;nbsp;구조를&amp;nbsp;만든다.&amp;nbsp;(온도를&amp;nbsp;알려주면&amp;nbsp;판매량을&amp;nbsp;예측해주는&amp;nbsp;기계) &lt;br /&gt;#&amp;nbsp;3&amp;nbsp;데이터로&amp;nbsp;모델을&amp;nbsp;학습(fit)한다.&amp;nbsp; &lt;br /&gt;#&amp;nbsp;4&amp;nbsp;모델을&amp;nbsp;이용한다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방법에 따라 레모네이드 판매를 예측하는 모델을 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;레모네이드 판매 예측 실습&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온도(레모네이드 온도 아님)에 따른 레모네이드의 판매량을 예측하는 실습을 해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;85&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PaPNb/dJMcai41GbX/neeYuqAUpbTWvkMmerA8B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PaPNb/dJMcai41GbX/neeYuqAUpbTWvkMmerA8B0/img.png&quot; data-alt=&quot;온도는 기온임&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PaPNb/dJMcai41GbX/neeYuqAUpbTWvkMmerA8B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPaPNb%2FdJMcai41GbX%2FneeYuqAUpbTWvkMmerA8B0%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;85&quot; height=&quot;111&quot; data-origin-width=&quot;85&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;온도는 기온임&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783945741646&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#라이브러리 사용
import pandas as pd
import tensorflow as tf

#================================
# [1] 레모네이드 판매 예측 모델
#================================
#1. 과거의 데이터를 준비합니다.
path = 'https://raw.githubusercontent.com/blackdew/tensorflow1/master/csv/lemonade.csv'
lemonade = pd.read_csv(path)

#독립변수(온도0와 종속변수(판매량) 분리
independent = lemonade[['온도']]
dependent = lemonade[['판매량']]&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;path는 기온과 판매량 데이터임. 읽어와서 독립변수와 종속변수를 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독립변수: 원인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종속변수: 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783945767971&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#2. 모델의 구조
X = tf.keras.layers.Input(shape=[1]) # Input(): 데이터가 들어오는 '입구'
Y = tf.keras.layers.Dense(1)(X)     # Dense(): 출구 레이어 (1) : 나가는 데이터가 1개
model = tf.keras.models.Model(X, Y) #: X,Y 레이어로 하나의 모델을 생성
model.compile(loss='mse')   # 만들어진 모델은 반드시 컴파일 해야 한다.&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;데이터가 들어오는 입구인 Input 레이어와 나가는 출구인 Dense 레이어를 설정한다.매개변수는 데이터의 개수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;loss = 'mse' 모델에 대한 손실함수는 mse(평균 제곱 오차)를 사용 &lt;br /&gt;즉, 학습과정에서 틀린 정도(오차)를 계산할 때 Mean Squared Error 방식을 사용하겠다! &lt;br /&gt;오차 정수가 0에 가까울 수록 알고리즘이 가중치 W를 미세하게 자동 조정한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&quot;1개의&amp;nbsp;입력을&amp;nbsp;받아서&amp;nbsp;(Input(shape=[1])), &lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;수학&amp;nbsp;연산을&amp;nbsp;거쳐&amp;nbsp;1개의&amp;nbsp;정답을&amp;nbsp;내보내는&amp;nbsp;(Dense(1))&amp;nbsp;기계를&amp;nbsp;조립하고(Model), &lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;오차를&amp;nbsp;줄여나갈&amp;nbsp;학습&amp;nbsp;전략을&amp;nbsp;세운다(compile).&quot;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783945972041&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 3. 데이터로 모델을 학습
print(&quot;\n모델 학습을 시작합니다...&quot;)
model.fit(independent, dependent, epochs=10)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fit -&amp;gt; 모델을 학습시키는 함수 epochs는 학습 횟수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783946105184&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 3. 데이터로 모델을 학습
print(&quot;\n모델 학습을 시작합니다...&quot;)
model.fit(independent, dependent, epochs=10)

#4 모델을 이용
print(&quot;\n---기존 데이터(independent) 예측 결과---&quot;)
print(model.predict(independent))

print(&quot;\n--- 내일 온도가 15도일 때의 판매량 예측 결과 ---&quot;)
predic_data = pd.DataFrame({'온도': [15]})&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;머신러닝 알고리즘&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지도학습 영역에는 회귀문제와 분류 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀 : 숫자로 된 결과를 예측 ( ex:레모네이드 판매량 예측)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 : 범주(category) 형태의 결과를 예측 (ex: 스팸메일 분류)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류와 회귀 문제를 풀기 위해서 다양한 머신러닝 알고리즘이 쓰입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 결정 트리(decision tree)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 랜덤 포레스트(random forest)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. k-최근접 이웃(KKN: k-nearest neighbors)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 서포트 벡터 머신(SVM: support vector machine)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 인공 신경망(artificial neural networks)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783948114370&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#공통 데이터 준비
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier

#붓꽃 데이터 불러오기

X,y=load_iris(return_X_y=True)# 일반적으로 원인은 대문자 x, 결과는 소문자 y
'''
붓꽃 데이터 구조
문제지 (X: 4가지 특징)
꽃받침 길이 (Sepal Length), 꽃받침 너비(Sepal Width), 꽃잎 길이(Petal Length), 꽃잎 너비 (Petal Width)
정답지 (y: 3가지 품종)
세토사 (Setosa):0, 버시칼라(Versicolor):1, 버지니카(Virginica):2

'''
#데이터를 학습용(80%) 과 테스트용 (20%)으로 무작위 분할 # 꽃받침의 넓이, 줄기의 길이로 어떤 품종인지 알려줌
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

#
import time
# 모델의 학습 진행율을 보기 위한 라이브러리
from tqdm import tqdm
###1 결정 트리(Decision Tree)
from sklearn.tree import DecisionTreeClassifier

#결정 트리 모델 생성 및 학습
dt_model = DecisionTreeClassifier(random_state=42)
print(&quot;모델 학습을 시작합니다...&quot;)
# 기존의 range(100) 대신 tqdm(range(100))을 사용.
#desc 옵션을 주면 진행률 바 앞에 원하는 문구를  띄울 수 있다.
for epoch in tqdm(range(100), desc=&quot;[Epoch 학습 중]&quot;):
    #이 부분에 실제 모델 학습 코드(fit 등)가 들어간다.
    dt_model.fit(X_train, y_train)
    #진행률 확인을 위해 가상으로 0.05초씩 쉬어간다.
    time.sleep(0.05)
print(&quot;학습이 완료되었습니다!&quot;)
# 테스트 데이터로 예측 및 정확도 확인
dt_pred = dt_model.predict(X_test)
print(f&quot;결정 트리 정확도 : {accuracy_score(y_test, dt_pred):.2f}&quot;)
### 2 랜덤 포레스트
from sklearn.ensemble import RandomForestClassifier

# 랜덤 포레스트 모델 생성 ( 나무를 100개 만들도록 설정) 및 학습
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)#n_estimators: 의사 결정 트리를 몇개 만들건지
dt_model = DecisionTreeClassifier(random_state=42)
print(&quot;모델 학습을 시작합니다...&quot;)
for epoch in tqdm(range(100),desc=&quot;[Epoch 학습 중&quot;):
    rf_model.fit(X_train, y_train)
    time.sleep(0.05)
print(&quot;모델 학습이 종료되었습니다...&quot;)
#예측 및 정확도 확인
rf_pred = rf_model.predict(X_test)
print(f&quot;랜덤 포레스트 정확도 : {accuracy_score(y_test, rf_pred):.2f}&quot;)

### 3. k-최근접 이웃( KNN: k-nearest neighbors)
from sklearn.neighbors import KNeighborsClassifier

# KNN 모델 생성 ( 가장 가까운 이웃 3개를 참고하도록 설정) 및 학습
knn_model = KNeighborsClassifier(n_neighbors=3)
print(&quot;모델 학습을 시작합니다...&quot;)
for epoch in tqdm(range(100),desc=&quot;[Epoch 학습 중&quot;):
    knn_model.fit(X_train, y_train)
    time.sleep(0.05)
print(&quot;모델 학습이 종료되었습니다...&quot;)
# 예측 및 정확도 확인
knn_pred = knn_model.predict(X_test)
print(f&quot;KNN 정확도: {accuracy_score(y_test,knn_pred):.2f}&quot;)

###4. 서포트 벡터 머신(SVM: support vector machine)
from sklearn.svm import SVC

#SVM 모델 생성 (분류 기준을 직선으로 설정) 및 학습
svm_model = SVC(kernel='linear',random_state=42)
print(&quot;모델 학습을 시작합니다.&quot;)
for epoch in tqdm(range(100),desc=&quot;[Epoch 학습 중]&quot;):
    svm_model.fit(X_train, y_train)
    time.sleep(0.05)
print(&quot;모델 학습이 종료되었습니다....&quot;)

# 예측 및 정확도 확인
svm_pred = svm_model.predict(X_test)
print(f&quot;SVM 정확도: {accuracy_score(y_test,svm_pred):.2f}&quot;)

###5. 인공 신경망 (Artificial Neural Networks)
from sklearn.neural_network import MLPClassifier

#인공 신경망 모델 생성 (최대 1000번 반복 학습하도록 설정) 및 학습
ann_model= MLPClassifier(max_iter=1000,random_state=42)
print(&quot;모델 학습을 시작합니다...&quot;)
for epoch in tqdm(range(100),desc=&quot;[Epoch 학습 중]&quot;):
    ann_model.fit(X_train, y_train)
    time.sleep(0.05)
print(&quot;모델 학습이 종료되었습니다....&quot;)
#예측 및 정확도 확인
ann_pred = ann_model.predict(X_test)
print(f&quot;인공 신경망 정확도: {accuracy_score(y_test,ann_pred):.2f}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;사이킷런(Scikit-learn)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 환경에서 머신러닝을 구현할 때 사용하는 가장 표준적이고 강력한 라이브러리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 수학 공식이나 알고리즘을 처음부터 짤 필요 없이, 결정 트리나 인공신경망 같은 다양한 모델들을 아주 짧은 코드로 불러와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 수 있게 해주는 '머신러닝 종합 공구 상자'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;머신러닝 워크플로우(Workflow of Machine Learning)&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙의 트리 구조 (결정 트리 형태)를 중심으로, 머신러닝 프로젝트가진행되는 전체적인작업 흐름을 8단계로 나타낸 도해.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계방향으로&amp;nbsp; 진행되는 각 단계의 한국어 의미는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/liJc0/dJMcaiqtD9d/zqvGcht4RzKkYXPBXgojIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/liJc0/dJMcaiqtD9d/zqvGcht4RzKkYXPBXgojIk/img.png&quot; data-alt=&quot;머신러닝 워크 플로우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/liJc0/dJMcaiqtD9d/zqvGcht4RzKkYXPBXgojIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FliJc0%2FdJMcaiqtD9d%2FzqvGcht4RzKkYXPBXgojIk%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;733&quot; height=&quot;715&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;머신러닝 워크 플로우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 전략 정의(De;fine Strategy)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 문제를해결할 것인지, 머신러닝을 통해 달성하고자 하는 목표와 계획을 세우는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 데이터 수집(Data Collection)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 해결에 필요한 다양한 데이터를 수집하고 모으는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 데이터 전처리(Data Preprocessing)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 해결에 필요한 다양한 데이터를 수집하고 모으는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 데이터 모델링(Data Modeling)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리된 데이터를 바탕으로, 문제 해결에 가장 적합한 머신러닝 알고리즘(모델)을 선택하고 설계하는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 학습 및 평가(Training and Evalution)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택한 모델에 데이터를 넣어 학습시키고, 그 모델이 얼마나 정확하게 작동하는지 시험해 보는(평가하는) 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 최적화(Optimization)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 결과를 바탕으로 모델의 성능을 최대한으로 끌어올리기 위해 미세하게 조정하고 개선하는 과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 배포(Deplayment)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성된 모델을 실제 서비스나 시스템에 적용하여 사용자들이 쓸 수 있게 만드는단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 성능 모니터링(Performancd Monitoring)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포된 모델이실제 환경에서도 에상대로 잘 작동하는지 지속적으로 관찰하고, 데이터 변화에 따른 성능 저하가없는지&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인하는 단계입니다. 이 결과에 따라 다시 전략을 수정하거나 데이터를 수집하는 등 순환하게 됩니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;보스턴 집값 예측 실습&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949026409&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 보스턴 집값 예측
import requests
# 라이브러리 사용
import tensorflow as tf
import pandas as pd
import certifi
from io import StringIO

#1. 과거의 데이터를 준비합니다.
path = 'https://raw.githubusercontent.com/blackdew/tensorflow1/master/csv/boston.csv'
response = requests.get(path, verify=certifi.where())
boston = pd.read_csv(StringIO(response.text))
print(boston.columns)
boston.head()&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;라이브러리 import와 데이터 준비.&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;다음은 boston.csv의 칼럼과 데이터의 일부이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRgfoY/dJMcaccJJ88/CKk3BKabFlvRZyFVy6Jy0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRgfoY/dJMcaccJJ88/CKk3BKabFlvRZyFVy6Jy0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRgfoY/dJMcaccJJ88/CKk3BKabFlvRZyFVy6Jy0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRgfoY%2FdJMcaccJJ88%2FCKk3BKabFlvRZyFVy6Jy0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;730&quot; height=&quot;193&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949093182&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#독립변수, 종속변수 분리
independent= boston[['crim','zn','indus','chas','nox','rm','age','dis','rad','tax','ptratio','b','lstat']]
dependent = boston[['medv']]&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;boston에서 독립변수와 종속변수로 분리한다. 13개의 조건에 따른 집값 예측이므로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;independent는 나머지 13개의 조건, dependent는 집값이다. 각각 506행 13열, 506행 1열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949214710&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#2. 모델의 구조
X=tf.keras.layers.Input(shape=[13]) # 독립변수(칼럼)
Y=tf.keras.layers.Dense(1)(X) # 종속변수
model = tf.keras.models.Model(X,Y)
model.compile(loss='mse')&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;독립변수와 종속변수로 Input 레이어와 Dense 레이어를 설정한다. 로스함수로 mse를 선택하고 컴파일 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keras는 tensorflow 안에 있는 딥러닝 모델을 쉽고 직관적으로 만들 수 있게 해주는 파이썬 라이브러리임.&lt;/p&gt;
&lt;pre id=&quot;code_1783949263048&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#3. 데이터로 모델을 학습
model.fit(independent,dependent,epochs=1000)&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;데이터들을 이용해서 모델을 1000번 학습시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949408247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#4. 모델을 이용

print(model.predict(independent[5:10]))
print(dependent[5:10])
print(model.get_weights())&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;[[26.129301] &lt;br /&gt;&amp;nbsp;[20.883503] &lt;br /&gt;&amp;nbsp;[17.717175] &lt;br /&gt;&amp;nbsp;[&amp;nbsp;9.075988] &lt;br /&gt;&amp;nbsp;[17.332752]]&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;&amp;nbsp;&amp;nbsp;medv &lt;br /&gt;5&amp;nbsp;&amp;nbsp;28.7 &lt;br /&gt;6&amp;nbsp;&amp;nbsp;22.9 &lt;br /&gt;7&amp;nbsp;&amp;nbsp;27.1 &lt;br /&gt;8&amp;nbsp;&amp;nbsp;16.5 &lt;br /&gt;9&amp;nbsp;&amp;nbsp;18.9&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;[array([[-0.08689677], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;0.07696634], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[-0.06071758], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;3.3436177&amp;nbsp;], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;1.6904595&amp;nbsp;], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;3.9580069&amp;nbsp;], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;0.01542883], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[-0.9243423&amp;nbsp;], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;0.14606974], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[-0.00957732], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;0.04639207], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;0.01565579], &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[-0.58180475]],&amp;nbsp;dtype=float32),&amp;nbsp;array([2.4192894],&amp;nbsp;dtype=float32)]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949562240&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
# 원핫인코딩
=&amp;gt; 범주형 데이터(Categorical Data)를 수치 데이터로 변환하는 가장 기본적인 방법
과일 종류인 [사과, 바나나, 포도]를 원-핫 인코딩으로 변환
범주 사과 바나나 포도
사과 1   0     0
바나나0  1     0
포도 0   0     1
붓꽃 데이터
===========================================
선형 회귀(Linear Regression): 연속적인 실수
로지스틱 회귀(Logistic Regression): 0 과 1 사이의 확률 / 분류 (Classification)

무엇을 예측하는가(결과값의 형태):

집값 예측, 주식 가격 예측, 기온 예측 &amp;lt;= 선형 회귀
스팸 메일 분류, 질병 양성/음성 판정, 합격/불합격 예측 &amp;lt;=로지스틱 회귀
======================================
선형 회귀(Linear Regression): 데이터의 경향성을 가장 잘 설명하는 하나의 '직선'을 찾는 모델
                            독립 변수(x)와 종속 변수(y) 사이의 선형 관계를 모델링
                y = wx + b
특징 : 예측값이 음의 무한대에서 양의 무한대까지
예시 : &quot;평수가 30평이고 역세권인 아파트의 가격은 얼마일까?&quot;를 예측 할 때 사용
결과값 : '5억원','7억 5천만 원'과 같이 연속된 수치
======================================
로지스틱 회귀(Logistic Regression) : 분류(Classification)문제에 주로 사용
선형회귀의 결과값을 0과 1 사이로 압축하여, 특정 사건이 발생할 '확률'로 변환
특징: 결과값이 항상 0과 1 사이의 확률
보통 기준값(예: 0.5)을 정해두고,
예측된 확률이 이보다 크면 클래스 1 (예: 스팸 맞음) 작으면 클래스 0 (예:스팸 아님)으로 분류
예시: &quot;이 메일이 스팸 메일일 확률은 얼마일까?&quot;를 예측할 때 사용
결과값이 '0.9'라면 90% 확률로 스팸 메일이라고 판단하여 스팸함으로 분류
======================================
다중 회귀 : 2개 이상의 독립변수를 종합하여 예측하는 모델.

y = w1x1 + w2x2 + w3x3 + wnxn + b(bias:편향)

단순 회귀: 2차원에서 하나의 직선 형태
다중 회귀: 3차원 공간에서 데이터를 잘 설명하는 하나의 평면
=====================================

'''&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;아이리스 품종 분류&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783949679915&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#아이리스 품종 분류
#라이브러리 사용
import tensorflow as tf
import pandas as pd
import requests
import certifi
from io import StringIO
from boston import independent

#1. 과거의 데이터를 준비
path ='https://raw.githubusercontent.com/blackdew/tensorflow1/master/csv/iris.csv'
response = requests.get(path, verify=certifi.where())
iris = pd.read_csv(StringIO(response.text))
iris.head()

#원핫인코딩
encoding=pd.get_dummies(iris)
encoding.head()
#-----------------------------------------
#칼럼이름 출력
print(encoding.columns)
#-----------------------------------------
#독립 변수(입력 데이터)와 종속 변수(정답 데이터)분리
#꽃잎길이,꽃잎폭,꽃받침길이,꽃받침폭,품종
independent=encoding[['꽃잎길이','꽃잎폭','꽃받침길이','꽃받침폭']]
dependent=encoding[['품종_setosa','품종_versicolor','품종_virginica']]
#-----------------------------------------
#2. 모델의 구조
X=tf.keras.layers.Input(shape=[4])
Y=tf.keras.layers.Dense(3,activation=&quot;softmax&quot;)(X)
model = tf.keras.models.Model(X,Y)
model.compile(loss='categorical_crossentropy',metrics=['accuracy'])
#-----------------------------------------
#3. 데이터로 모델을 학습
model.fit(independent,dependent,epochs=100)
#-----------------------------------------
#4. 모델을 이용
print(model.predict(independent[:5]))
'''

'''
print(dependent[:5])
print(model.predict(independent[-5:]))
print(dependent[-5:])
#-----------------------------------------
print(model.get_weights())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;최소자승법&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 여러 개의 별들이 흩어져 있을 때, 그 별들의 대략적인 흐름을 가장 잘 보여주는 하나의 선을 긋는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점들의 흐름을 가장 잘 나타내는 하나의 직선을 그리는것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 어떤 직선을 그렸을&amp;nbsp; 때, 실제 점들은그 직선 위에 딱 놓이지 않고 약간씩 떨어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 떨어진 거리가 바로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;오차.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;최소자승법은 이 모든 점들의 오차를 제곱해서 더한 값을 가장 작게 만드는 직선을 찾는 방법.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흩어진 데이터들을 가장 잘 설명하는 대표적인 선을 찾아주는 아주 유용한 방법이다. 이 선을 이용하면 앞으로의 경향을 예측하거나 데이터들 사이의 관계를파악하는 데 도움을 받을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;회귀 계수&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 어떤 원인(독립변수)이 결과(종속변수)에 얼마나 영향을 미치는지 알려주는 숫자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 공부시간과 시험점수의 관계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀 계수가 5라면? 공부 시간을 1시간 늘릴때마다 시험점수가 평균적으로 5점씩 올라간다는 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀 계수가 0에 가깝다면 원인이 결과에 거의 영향을 미치지 않는다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;SSE(오차 제곱 합)&lt;/b&gt;&lt;/i&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;최소자승법으로 찾은 &quot;가장 잘 맞는 선&quot;이 실제 데이터들을 얼마나 잘 설명하는지를 알려주는 숫자. 마치 우리가 그린 선들이 실제 점들과 얼마나 떨어져 있는지, 그 떨어진 정도를 종합적으로 보여주는 지표.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Sum of Squares(제곱 합)&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; 이 오차들을 그냥 더하면 양수 오차와 음수 오차가 서로 상쇄될 수 있다. 그래서 각 오차를 제곱해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; 제곱하면 모든 오차가 양수가 되고, 큰 오차는 더 크게 벌주는 효과도 있다. 그리고 이 제곱된 모든 오차들을 다 더한 값이 바로 &lt;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&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;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; 제곱 합이 작다 -&amp;gt; 실제 데이터들과 우리가 그린 선이 별로 떨어져 있지 않다는 뜻.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; 제곱 합이 크다 -&amp;gt; 실제 데이터들과 우리가 그린 선이 많이 떨어져 있다는 뜻.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소자승법은 이 SSE 값을 가장 작게 만드는 선을 찾는 방법이다.&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;SST = 분산 *&amp;nbsp; N&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;경사하강법&lt;/b&gt;&lt;/i&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;현재 위치에서 가장 가파르게 내려가는 방향을 찾아서 조금씩 이동. 이 &quot;가장 가파르게 내려가는 방향&quot;이 바로 경사를 의미. 그리고 이 방향으로 하강하는 방법을 쓰는것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상태에서 결과가 가장 좋아지는 방향(가장 가파르게 내려가는 방향)을 찾는다. 그 방향으로 아주 조금씩 움직인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/6</guid>
      <comments>https://bb0ng2.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 13 Jul 2026 22:53:33 +0900</pubDate>
    </item>
    <item>
      <title>0710 Numpy</title>
      <link>https://bb0ng2.tistory.com/5</link>
      <description>&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;#Numpy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 넘파이는 파이썬에서 수치를 계산하는 라이브러리이다. 머신러닝과 데이터 과학 분야에서 넘파이는 필수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝의 모든 데이터는 결국 숫자로 이루어진 행렬로 표현되며, 넘파이는 이 대규모 행렬 연산을 압도적인 속도로 처리해 주는 핵심 라이브러리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;머신러닝&amp;nbsp;코드를&amp;nbsp;작성하거나&amp;nbsp;논문을&amp;nbsp;구현할&amp;nbsp;때&amp;nbsp;가장&amp;nbsp;자주&amp;nbsp;쓰이는&amp;nbsp;핵심&amp;nbsp;기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 다차원 배열 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666241148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
머신러닝에서 데이터셋은 대개 2차원(행과 열) 또는 3차원(이미지: 가로, 세로, 채널) 배열.

np.array(): 리스트를 NumPy 배열로 변환

np.zeros(), np.ones(): 0 또는 1로 채워진 가중치(Weights) 초기화 시 사용

np.arange(): 일정 간격의 수열 생성 (인덱싱이나 루프 제어 시 유용)
'''
###-----------------------------------------------------------------
import numpy as np

# 2차원 데이터셋 예시 (샘플 2개, 특성 3개)
X = np.array([[1.0, 2.0, 3.0], 
              [4.0, 5.0, 6.0]])&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. 차원과 모양 변경 (Reshaping)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666350335&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''

머신러닝 모델에 데이터를 입력할 때
가장 에러가 많이 나는 부분이 바로 데이터의 '모양(Shape)'이 맞지 않을 때.

X.shape: 배열의 구조(차원) 확인 
(예: (100, 32, 32) -&amp;gt; 100장의 32x32 이미지)

X.reshape(): 데이터의 개수는 유지하되 차원을 변경 
(예: 2D 이미지를 1D로 평탄화)

Tip: -1을 넣으면 남은 차원으로부터 크기를 자동으로 계산. 
X.reshape(-1, 1)은 머신러닝 레이블 벡터를 만들 때
'''
#----------------------------------------------
# 2x3 배열을 6x1 배열로 변환
X_reshaped = X.reshape(-1, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-1의 의미 : &quot;이 자리의 크기는 너가 알아서 계산해&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2x3 배열이므로 reshape(-1,1)을 하면 열 크기를 1로 고정해서 자동으로 6행 1열이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp;브로드캐스팅&amp;nbsp;(Broadcasting)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666601101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''

머신러닝에서 모든 데이터 샘플에 동일한 연산
(예: 평균 빼기, 스케일링)을 적용할 때 루프를 돌릴 필요가 없다. 
NumPy는 서로 모양이 다른 배열 간의 연산을 자동으로 확장해서 처리!
'''
#----------------------------------------------
# 모든 데이터에 일괄적으로 편향(Bias) 더하기
biases = np.array([0.5, 1.0, 1.5])
result = X + biases  # 각 행에 biases가 알아서 더해짐

#행이 맞지않아도 자동으로 더해짐&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;biases를 X의 행에 각각 더해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.&amp;nbsp;벡터&amp;nbsp;및&amp;nbsp;행렬&amp;nbsp;연산&amp;nbsp;(Linear&amp;nbsp;Algebra)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666653559&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''

딥러닝의 핵심은 행렬 곱(Dot Product). 
신경망(CNN)의 순전파(Forward) 연산은 대부분 행렬 곱으로 이루어진다.
순전파는 정방향으로 가는거 -&amp;gt;
역전파는 역방향으로 가는거 &amp;lt;-
np.dot(A, B) 또는 A @ B: 행렬 곱셈 (내적)

A.T: 전치 행렬 (Transposed Matrix, 행과 열을 바꿈)
'''
#----------------------------------------------
W = np.array([[0.1, 0.2], 
              [0.3, 0.4], 
              [0.5, 0.6]]) # 가중치 행렬 (3x2)

# 행렬 곱 연산 (2x3) @ (3x2) = (2x2)
output = X @ W&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 집계 함수와 축(Axis) 개념&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666710680&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''

손실 함수(Loss Function)를 계산하거나 
데이터의 특성별 평균을 구할 때 사용. 
axis 개념을 정확히 이해하는 것이 중요!!!!!

axis=0: 열(Column) 기준 연산 
(행을 따라 내려가며 연산 -&amp;gt; 각 특성별 평균)

axis=1: 행(Row) 기준 연산 
(열을 가로지르며 연산 -&amp;gt; 각 샘플별 합계)
'''
#----------------------------------------------
# 각 특성(열)별 평균 계산 -&amp;gt; 머신러닝 정규화(Normalization)의 기초
mean = np.mean(X, axis=0)&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;6. 조건문과 인덱싱 (Filtering)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666749607&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''

데이터셋에서 특정 조건(예: 이상치 제거, 특정 클래스 추출)을 만족하는 
데이터만 뽑아낼 때 사용.

불리언 인덱싱(Boolean Indexing): 조건식의 결과가 True인 요소만 추출

np.where(조건, 참일 때 값, 거짓일 때 값): 조건에 따른 데이터 수정 또는 인덱스 찾기
'''
#----------------------------------------------

# 3보다 큰 데이터만 추출
filtered_X = X[X &amp;gt; 3]

# 3보다 큰 값은 1로, 나머지는 0으로 변환 (이진 분류 레이블링 등에 유용)
binary_labels = np.where(X &amp;gt; 3, 1, 0)&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;where은 삼항연산자랑 똑같음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#행렬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783666853187&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 전치 행렬 (Transpose, .T)
&quot;&quot;&quot;

&quot;가로줄과 세로줄을 통째로 맞바꾸기&quot;

머신러닝에서는 데이터의 형태(shape)를 맞추기 위해 행렬을 휙 돌려야 할 때가 정말 많다. 
가로(행)를 세로(열)로, 세로(열)를 가로(행)로 뒤집는 것을 전치

결과: 원래 [1, 2, 3]이었던 가로줄이 세로줄 [1, 2, 3]으로 변경
&quot;&quot;&quot;
#----------------------------------------------

import numpy as np

# 2행 3열짜리 행렬 생성
A = np.array([[1, 2, 3],
              [4, 5, 6]])

# .T를 붙여서 뒤집기
A_transposed = A.T

print(&quot;원본 A (2x3):\n&quot;, A)
print(&quot;전치 후 A.T (3x2):\n&quot;, A_transposed)





#----------------------------------------------
# 2. 행렬 곱 (Dot Product, @)
&quot;&quot;&quot;

&quot;앞 행렬의 가로줄과 뒤 행렬의 세로줄을 교차하며 곱하고 더하기&quot;

인공지능 신경망이 '예측'을 할 때 입력 데이터와 가중치(Weight)를 곱하는 핵심 연산. 
일반 곱셈(*)처럼 원소끼리 그냥 곱하는 게 아니라, 가로와 세로를 교차하며 곱해 합산.

규칙: 앞 행렬의 가로 길이와 뒤 행렬의 세로 길이가 반드시 같아야 연산이 가능!! 
((2x3) @ (3x2) = (2x2))

계산 원리: 결과 창의 첫 번째 칸은 (1*1) + (2*3) + (3*5) = 22
&quot;&quot;&quot;
#----------------------------------------------

# 2행 3열 행렬 (예: 데이터 2개)
X = np.array([[1, 2, 3],
              [4, 5, 6]])

# 3행 2열 행렬 (예: 가중치)
W = np.array([[1, 2],
              [3, 4],
              [5, 6]])

# 행렬 곱 실행
result = X @ W

print(&quot;행렬 곱 결과 (2x2):\n&quot;, result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#머신러닝 데이터 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783667052819&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#----------------------------------------------------------------
# 요약하자면,
# 머신러닝 데이터 처리는
# &quot;.T로 돌리고,
# @로 곱하고,
# 브로드캐스팅으로 알아서 맞춰 더한 뒤,
# axis로 원하는 방향의 평균을 내는 과정&quot;의 반복
#----------------------------------------------------------------

#----------------------------------------------
# 이미지 분석할때

'''
1. 컴퓨터에게 이미지는 그냥 '숫자 행렬'이다
   우리 눈에는 예쁜 사진으로 보이지만, 
   컴퓨터는 이미지를 거대한 숫자 바둑판(행렬)으로 인식.
   가로 세로 픽셀마다 밝기나 색상(0~255)이 숫자로 채워져 있다.
   예를 들어, 가로 28픽셀, 세로 28픽셀짜리 작은 손글씨 이미지는 
   컴퓨터에게 28 X 28 크기의 행렬일 뿐이다.
   
2. 이미지 분석의 핵심: 특징 추출 (합성곱, Convolution)
   이미지를 분석하려면 사물의 '테두리(선)', '색상의 변화', '특정 모양' 같은 특징을 찾아내야 한다. 
   이때 인공지능은 필터(Filter 또는 Kernel)라고 불리는 
   작은 돋보기 행렬을 사용하는데, 
   여기서 바로 행렬곱이 쓰인다.
   도장 찍기: 이미지 행렬 위에 작은 필터 행렬(예:3X3)을 포갠다.
   행렬곱 연산: 포개어진 부분의 이미지 숫자들과 필터의 숫자들을 순서대로 곱하고 모두 더한다.
   (우리가 방금 배운 그 행렬곱 방식입니다!)
   특징 맵 생성: 이 돋보기를 옆으로, 아래로 한 칸씩 이동하며 이미지 전체에 도장을 찍으면, 
   특정 특징(예: 세로선)만 뚜렷하게 강조된 새로운 행렬이 만들어진다.
   
     딥러닝에서 이미지 분석에 최고로 꼽히는 
   **CNN(합성곱 신경망)**의 '합성곱'이 바로 이 필터를 이용한 행렬곱 연산을 의미.  
   
3. 최종 판단: 분류 (Fully Connected Layer)
필터를 통해 이미지에서 
&quot;고양이 귀 모양&quot;, &quot;고양이 수염 모양&quot; 같은 특징들을 다 뽑아내고 나면, 
인공지능은 마지막으로 &quot;그래서 이 사진이 고양이일 확률이 몇 %인가?&quot;를 계산해야 한다.

이때도 마찬가지로 
추출된 특징 데이터 행렬과 
인공지능이 학습한 가중치(Weight) 행렬을 
최종적으로 거대하게 행렬곱(@) 하여 
&quot;고양이: 98%, 강아지: 2%&quot; 같은 결론을 도출한다.
'''
#----------------------------------------------

#----------------------------------------------------------------
# 요약하자면,
# 컴퓨터가 이미지를 분석하는 과정은
# &quot;이미지 행렬에
# 필터 행렬을 계속 곱해서(@)
# 중요한 특징을 짜낸 후,
# 마지막 행렬곱을 통해
# 정답을 맞추는 과정&quot;이라고 볼 수 있다.   
#----------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/5</guid>
      <comments>https://bb0ng2.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 10 Jul 2026 09:48:07 +0900</pubDate>
    </item>
    <item>
      <title>0709 파이썬 기초 문법 복습</title>
      <link>https://bb0ng2.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자료구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# List&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중값을 저장, 순서가 있고 index가 자동으로 부여 (index는 0~1씩 증가) C++의 vector와 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형식&amp;nbsp;:&amp;nbsp;리스트명&amp;nbsp;=&amp;nbsp;[],&amp;nbsp;리스트명&amp;nbsp;=&amp;nbsp;['데이터','데이터','데이터']&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽-&amp;gt;오른쪽 : 0,1,2,3,4, &lt;br /&gt;오른쪽-&amp;gt; 왼쪽 :-4, -3, -2,-1&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783654742208&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#-------------------------------------------------------
## 데이터 추가 : append()/ insert()/ extend()
menu = [&quot;페르소나&quot;]
menu.append(&quot;릴레이&quot;) # ['페르소나', '릴레이']
menu.insert(1, &quot;마중물&quot;) #  ['페르소나', '마중물', '릴레이']
menu.extend([&quot;수필&quot;, &quot;시각&quot;]) # ['페르소나', '마중물', '릴레이', '수필', '시각']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;append는 C++의 vector.push_back()과 비슷함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;insert를 통해 삽입하고 싶은 위치에 삽입 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extend 함수로 한번에 여러 데이터 삽입 가능.&lt;/p&gt;
&lt;pre id=&quot;code_1783654956152&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;items = [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;B&quot;]
items.remove(&quot;B&quot;) # ['A', 'C', 'B'] : 맨 처음 만나는 데이터만 삭제
last = items.pop() # items :  ['A', 'C'] / last : 'B'
items.clear()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;remove는 전달한 값과 같은 값을 갖는 요소를 삭제 함. 근데 맨 처음 만나는 데이터만 삭제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop()은 맨 뒤 요소 삭제. 마치 C++ queue.pop()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clear은 리스트의 데이터만 삭제. 메모리는 남아있음&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;데이터&amp;nbsp;위치(index)&amp;nbsp;및&amp;nbsp;동일&amp;nbsp;데이터&amp;nbsp;&amp;nbsp;갯수&amp;nbsp;:&amp;nbsp;index(x)&amp;nbsp;/&amp;nbsp;count(x)&amp;nbsp;/&amp;nbsp;len(리스트명)&lt;/p&gt;
&lt;pre id=&quot;code_1783655245644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# index(x): 리스트에서 값 x가 있는 첫 번째 위치(인덱스)를 반환. 없으면 오류 발생!!!!
# count(x): 리스트에 값 x가 몇 개 들어있는지 개수
tech = [&quot;Python&quot;, &quot;Java&quot;, &quot;Python&quot;, &quot;C&quot;]
print(tech.index(&quot;Python&quot;)) # 맨 처음 만나는 데이터의 index만 반환
print(tech.count(&quot;Python&quot;)) # 2 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;len은 C++의 v.size()랑 비슷함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783655302219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;## 정렬 및 뒤집기 : sort() / reverse()
# sort(): 리스트의 요소를 오름차순으로 정렬 / 주의 원본 데이터의 순서가 바뀐다.
# reverse(): 정렬과 상관없이 현재 리스트의 순서를 그대로 거꾸로 뒤집는다.
numbers = [3, 1, 4, 5, 2]
numbers.sort()    # 오름차순 정렬 -&amp;gt; [1, 2, 3, 4, 5]
numbers.sort(reverse=True)    # 내림차순 정렬 -&amp;gt;  [5, 4, 3, 2, 1]
numbers.reverse() # 뒤집기 -&amp;gt; [5, 4, 3, 2, 1]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783659123839&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 복사 : copy() / 리스트를 똑같이 복사하여 새로운 리스트를 생성
# =&amp;gt; 원본 리스트를 보존하고 똑같은 복사본으로 작업하고 싶을 때 사용
original = [1, 2, 3]
clone = original.copy() # [1, 2, 3]&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;clone=original이라고만 하면 깊은 복사가 이루어져서 위험함. 복제본을 만드려면 반드시 copy()함수를 사용할것.&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;#Dictionary&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;&quot;키(key) - 값(value)&quot; 쌍으로 데이터를 저장하는 자료구조. C++의 map과 비슷함. 근데 value 값에 모든 타입이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#생성&lt;/p&gt;
&lt;pre id=&quot;code_1783659363255&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터가 존재하는 딕셔너리 생성
platform_info = {
    &quot;name&quot;: &quot;당목담글&quot;,
    &quot;slogan&quot;: &quot;사람과 마음을 연결하는 세상에서 가장 따뜻한 이어읽기 플랫폼&quot;,
    &quot;components&quot;: [&quot;페르소나&quot;, &quot;마중물&quot;, &quot;릴레이&quot;]
}

# 빈 딕셔너리 생성
empty_dict = {}
empty_dict2 = dict()&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;#get()&lt;/p&gt;
&lt;pre id=&quot;code_1783659801924&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# get()을 사요하는 경우
print(platform_info.get(&quot;launch_year&quot;)) # None  인덱스로 접근하면 오류발생
print(platform_info.get(&quot;launch_year&quot;, 2026)) # 2026&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get()을 사용하면 없는 키 값을 읽을 때 오류가 나지 않는다.&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 id=&quot;code_1783660058313&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
keys(): 딕셔너리의 모든 Key만 모아서
values(): 딕셔너리의 모든 Value만 모아서
items(): 딕셔너리의 (Key, Value) 쌍을 튜플로 묶어서
=&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660172524&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pop(key): 특정 Key에 해당하는 쌍을 꺼내면서(반환) 삭제
# del 딕셔너리[key]: 특정 Key의 쌍을 바로 삭제
# clear(): 딕셔너리의 모든 데이터
sample = {&quot;A&quot;: 1, &quot;B&quot;: 2}
val = sample.pop(&quot;A&quot;) # {'B': 2} / val =&amp;gt; 1
del sample[&quot;B&quot;] # {}

sample = {&quot;A&quot;: 1, &quot;B&quot;: 2, &quot;c&quot;: 2}
sample.clear() #  {} =&amp;gt; 딕셔너리는 그대로.. 내부 데이터(key:value)들만 삭제.&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_1783660238603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 딕셔너리 활용법
# setdefault()와 collections.defaultdict(초기값 자동 설정)
'''
딕셔너리를 사용할 때 가장 자주 하는 작업 중 하나
&quot;존재하지 않는 키면 값을 0(또는 빈 리스트)으로 만들고, 
이미 존재하는 키면 값을 업데이트하기&quot;
'''
## setdefault(key, default) 활용 : 단어 빈도수(Count) 계산
words = [&quot;릴레이&quot;, &quot;마중물&quot;, &quot;릴레이&quot;, &quot;페르소나&quot;, &quot;마중물&quot;, &quot;릴레이&quot;]
counter = {}

for word in words:
    # 키가 없으면 0을 먼저 넣고, 있으면 원래 값에 +1
    counter[word] = counter.setdefault(word, 0) + 1
#  {'릴레이': 3, '마중물': 2, '페르소나': 1}&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;defaultdict&amp;nbsp;활용&lt;/p&gt;
&lt;pre id=&quot;code_1783660379181&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# collections 모듈의 defaultdict를 사용하면 
# 아예 딕셔너리 생성 단계부터 &quot;기본 데이터 타입&quot;을 지정할 수 있다!!
# 존재하지 않는 키를 조회하면 자동으로 빈 리스트([])를 기본값으로 생성
from collections import deque, Counter, defaultdict
user_history = defaultdict(list) # defaultdict(list, {})

user_history[&quot;김철수&quot;].append(&quot;수필 텍스트 읽기&quot;)
user_history[&quot;이영희&quot;].append(&quot;목소리 기부 참여&quot;)
user_history[&quot;김철수&quot;].append(&quot;이어읽기 플랫폼 접속&quot;)
'''
 defaultdict(list, {'김철수': ['수필 텍스트 읽기', 
                               '이어읽기 플랫폼 접속'], 
                    '이영희': ['목소리 기부 참여']})&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;딕셔너리 컴프리헨션 (Dictionary Comprehension) : 내포리스트 형태&lt;/p&gt;
&lt;pre id=&quot;code_1783660439546&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 구성 요소 리스트를 기반으로 {요소명: 글자수} 딕셔너리 생성
components = [&quot;페르소나&quot;, &quot;마중물&quot;, &quot;릴레이&quot;]

component_lengths = {comp: len(comp) for comp in components}
print(component_lengths)
#=&amp;gt;  {'페르소나': 4, '마중물': 3, '릴레이': 3}

# 조건문(if)과 함께 사용하기 (글자 수가 3개인 것만 골라내기)
filtered_dict = {k: v for k, v in component_lengths.items() if v == 3}
#=&amp;gt; {'마중물': 3, '릴레이': 3}&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;collections.Counter&amp;nbsp;:&amp;nbsp;데이터의&amp;nbsp;개수를&amp;nbsp;세어서&amp;nbsp;딕셔너리&amp;nbsp;형태로&amp;nbsp;변환해&amp;nbsp;주는&amp;nbsp;파이썬&amp;nbsp;내장&amp;nbsp;라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660589516&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 주로 알고리즘 테스트나 데이터 분석에서 많이 사용
from collections import Counter


votes = [&quot;페르소나&quot;, &quot;릴레이&quot;, &quot;페르소나&quot;, &quot;마중물&quot;, &quot;페르소나&quot;, &quot;릴레이&quot;]
voted_counter = Counter(votes)
# =&amp;gt; Counter({'페르소나': 3, '릴레이': 2, '마중물': 1})

# 가장 많이 등장한 상위 N개 뽑기
print(voted_counter.most_common(1)) # [('페르소나', 3)]&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;딕셔너리&amp;nbsp;병합&amp;nbsp;및&amp;nbsp;수정&amp;nbsp;:&amp;nbsp;update()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660697223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 두 개의 딕셔너리를 하나로 합치거나, 여러 값을 한 번에 수정할 때 update() 메서드를 사용
# 겹치는 키가 있다면 새로 들어온 값으로
platform_base = {&quot;name&quot;: &quot;당목담글&quot;, &quot;type&quot;: &quot;플랫폼&quot;}
platform_detail = {&quot;type&quot;: &quot;이어읽기 플랫폼&quot;, &quot;launch_year&quot;: 2026}

platform_base.update(platform_detail)
# =&amp;gt;  {'name': '당목담글', 'type': '이어읽기 플랫폼', 'launch_year': 2026}
# =&amp;gt; platform_base에 platform_detail을 덮어쓰기&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;#Tuple&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;형식 : TUPLE명 = (데이터, 데이터... ) &lt;br /&gt;특징 : 리스트 처럼 순서는 있지만 데이터에 재한 수정이 불가!!! &lt;br /&gt;따라서 LIST보다 처리 속도가 빠르다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660809547&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;platform_core = (&quot;페르소나&quot;, &quot;마중물&quot;, &quot;릴레이&quot;)

numbers = 1, 2, 3 # 도 가능
# =&amp;gt;  (1, 2, 3)

#주의사항 :  데이터가 1개만 존재하는 겨우 마지막에 반드시,를 붙여야 한다.
single_element = (5,)
not_a_tuple = (5) # 는 튜플이 아닌 그냥 정수로..
# =&amp;gt; 5


sample = (10, 20, 30)
print(sample[0])

sample[0] = 99 # TypeError : 'tuple' object does not support item assignment
del sample[0] # TypeError : 'tuple' object does not support item assignment&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;튜플의&amp;nbsp;활용법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660892049&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 다중 변수 할당
service, phrase = (&quot;당목담글&quot;, &quot;사람과 마음을 연결하는 세상에서 가장 따뜻한 이어읽기 플랫폼&quot;)
# service : &quot;당목담글&quot;
# phrase : &quot;사람과 마음을 연결하는 세상에서 가장 따뜻한 이어읽기 플랫폼&quot;

# 2. 변수의 값 교환
a = 10
b = 20

a, b =b, a
# a : 20
# b : 10

# 3. 함수 내에서 다중 값 반환
def get_platform_info():
    name = &quot;당목담글&quot;
    year = 2026
    return name, year #dtype : tuple

result = get_platform_info() # ('당목담글', 2026)

# 튜플은 프로그램이 실행되는 동안 절대 변하면 안된느 값을 다룰때 많이 사용
# 예 : 1년의 12달, 위도와 경도, 플랫폼 고정 메뉴 이름,주민등록번호 등...&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;#Set&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;셋(Set,&amp;nbsp;집합)은&amp;nbsp;수학의&amp;nbsp;집합&amp;nbsp;개념을&amp;nbsp;코드로&amp;nbsp;구현한&amp;nbsp;자료구조 &lt;br /&gt;중복을 허용하지 않는다: 아무리 똑같은 값을 여러 번 넣어도 set 안에는 딱 하나 &lt;br /&gt;순서가 없다 (Unordered): 데이터가 들어간 순서를 기억하지 않기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트처럼 인덱싱(set[0])으로 특정 값을 꺼낼 수 없다!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660938837&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;components = {&quot;페르소나&quot;, &quot;마중물&quot;, &quot;릴레이&quot;, &quot;페르소나&quot;}

# **** 주의: 빈 set을 만들 때는 {}가 아닌 set()을 사용!!!
# 이유 : 빈 딕셔너리가 만들어지기 때문!!!
empty_set = set()&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;데이터&amp;nbsp;추가/삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783660976892&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
add(x): 요소 1개 추가
update([x, y]): 여러 요소 한 번에 추가 / 이 때 리스트, 튜플로 전달됨!
remove(x): 특정 요소 삭제 / 주의 : 삭제할 값이 없을 경우, 오류 발생!!
discard(x): 특정 요소 삭제 /  삭제할 값이 없어도 오류 발생 하지 않음(오류 무시!)
'''
tech = {&quot;Python&quot;, &quot;Java&quot;}

tech.add(&quot;C++&quot;) #  {'C++', 'Java', 'Python'}
tech.update([&quot;Go&quot;, &quot;Kotlin&quot;]) # {'C++', 'Go', 'Java', 'Kotlin', 'Python'}
tech.remove(&quot;Java&quot;) #  {'C++', 'Go', 'Kotlin', 'Python'}&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;# Set만의 특별한 함수들&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783661002116&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;## 수학적 집합 연산 : 교집합/ 차집합/ 합집합
set_A = {1, 2, 3, 4}
set_B = {3, 4, 5, 6}

# 교집합(iNTERSECTION)
print(set_A &amp;amp; set_B) # {3, 4}
print(set_A.intersection(set_B))  # {3, 4}

# 합집합 (union) : 만약 중복데이터가 있을 경우 하나만...
print(set_A | set_B) # {1, 2, 3, 4, 5, 6}
print(set_A.union(set_B)) # {1, 2, 3, 4, 5, 6}

#차집합(Diffence) : 아애의 경우, A에는 있지만 B에는 없는 경우
print(set_A - set_B) # {1, 2}
print(set_A.difference(set_B))  # {1, 2}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;##&amp;nbsp;****&amp;nbsp;실무/알고리즘에서&amp;nbsp;Set을&amp;nbsp;사용하는&amp;nbsp;경우***&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783661037018&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 리스트에서 중복 데이터 단 한 줄로 제거
duplicated_list = [&quot;릴레이&quot;, &quot;마중물&quot;, &quot;릴레이&quot;, &quot;페르소나&quot;, &quot;마중물&quot;]
unique_list = list(set(duplicated_list)) # ['릴레이', '페르소나', '마중물']

# 2. 특정 데이터가 존재하는지 확인 (in 연산자)
blacklist = {&quot;user123&quot;, &quot;bad_bot&quot;, &quot;spammer&quot;}
if &quot;bad_bot&quot; in blacklist:
    print(&quot;접근을 차단합니다.&quot;) # 접근을 차단합니다.
    
# *** Set은 딕셔너리와 같은 원리(해시 테이블)로 동작하기 때문에 
#     데이터가 1,000만 개가 있어도 단 한 번에 값을 찾아낸다!!!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#해시 테이블&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783661095839&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
해시 테이블(Hash Table)에 대하여 
&quot;어떤 데이터(Key)를 주더라도 단 한 번에 그 위치를 찾아내는 마법의 저장소&quot;

원리
해시 함수(Hash Function)라는 특별한 수학적 함수를 거쳐 데이터의 방 번호(인덱스)를 결정
1. Key입력 : 검색하거나 저장하고 싶은 데이터(&quot;bad_bot&quot;)
2. 해시함수 통과 : 해시 함수가 글자를 숫자로 이루어진 고유한 고윳값(해시 코드)으로 변환
3. 인덱스 변환 : 이 숫자를 배열의 크기로 나눈 나머지 등을 이용해 실제 저장될 방 번호(인덱스)를 구한다
4. 값 저장/조회 : 그 방 번호(버킷)에 데이터(Value)를 바로 저장, 추출
의 단계를 거친다.
'''
#실제 생활과 비교 설명
'''
책이 1,000만권이 있는 도서관에서 책을 찾을 경우,
리스트 처럼 1번 부터 찾기는 힘들다
하지만, 책 이름을 컴퓨터에서 검색하여 &quot;3층 c열에 있다&quot;라고 결과를 확인한 후, 
찾으면 바로 찾을 수 있다.
컴퓨터에서 검색 : 해시 함수
이와 같은 과정이 해시 테이블

# 해시 테이블의 장점 : 빠르다!
# 해시 테이블의 단점 : 미리 공간을 만들어놓아야 하기 때문에 메모리를 많이 차지 한다!

'''&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 출력 : print()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 입력 : input()&lt;/p&gt;
&lt;pre id=&quot;code_1783646456753&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name = '파이쏭'
print(name)
print(f'변수를 이용할 경우 %s' % name)
# %s : 문자열, %d: 숫자
#  정수의 경우 : int()로, 소숫점일 경우 float()로 변환
# 기본값 (각각의 값을 출력할 때 공백으로 구분)
print(&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;)
# =&amp;gt; 출력:apple banana cherry

# 구분 기호를 이용할 경우 : sep=&quot;구분자&quot;
print(&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;, sep=&quot; / &quot;)
# =&amp;gt; 출력: apple / banana / cherry

# 하나의 print()에서 줄바꿈 대신 공백을 붙여서 한 줄로 출력
print(&quot;Hello&quot;, end=&quot; &quot;)
print(&quot;World&quot;)
# =&amp;gt; 출력: Hello World
# 자주 사용되는 출력 특수 문자(이스케이프 문자)
\n: 줄바꿈
\t: 탭(Tab)만큼 띄우기
\\: 역슬래시(\) 문자 자체를 출력&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;형식을&amp;nbsp;지정하여&amp;nbsp;출력할&amp;nbsp;경우&amp;nbsp;:&amp;nbsp;f-string&amp;nbsp;활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;f-string : 파이썬에서 문자열 안에 변수 값을 바로 넣을 수 있게 해주는 문법. &quot;formatted string&quot; 코드의&amp;nbsp;가독성&amp;nbsp;때문에&amp;nbsp;추가된&amp;nbsp;기능&lt;/p&gt;
&lt;pre id=&quot;code_1783646661744&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;service_name = &quot;당목담글&quot;
slogan = &quot;사람과 마음을 연결하는 세상에서 가장 따뜻한 이어읽기 플랫폼&quot;

print(f&quot;[{service_name}]은 {slogan}입니다.&quot;)

age = 20

# 1. 옛날 방식 (문자열 더하기) - 숫자는 str()로 형변환을 해야 해서 번거로움
print(&quot;내 나이는 &quot; + str(age) + &quot;살입니다.&quot;)

# 2. .format() 방식 - 문자열과 변수의 위치가 떨어져 있어 한눈에 보기 힘듦
print(&quot;내 나이는 {}살입니다.&quot;.format(age))

# 3. f-string 방식 - 직관적이고 직관적이며 간결함!
print(f&quot;내 나이는 {age}살입니다.&quot;)

a = 10
b = 20
print(f&quot;{a} + {b}의 결과는 {a + b}입니다.&quot;)
# =&amp;gt; 10+20의 결과는 30입니다.

name = &quot;python&quot;
print(f&quot;대문자로 변환: {name.upper()}&quot;)
# =&amp;gt; 출력: 대문자로 변환: PYTHON

## 소수점 자리수 지정
pi = 3.1415926535
print(f&quot;원주율: {pi:.2f}&quot;)
# =&amp;gt; 소소정 두자리까지만 출력
#실제 데이터는 그대로 있고, 출력할 때만 두자리까지 출력
# 금융권등에서 통장에 출력하는 경우 많이 사용
# 통신사의 요금 청구서 출력시에도 사용됨

## 천 단위 콤마(,) 넣기
money = 5000000
print(f&quot;잔액: {money:,}원&quot;)
# =&amp;gt; 출력: 잔액: 5,000,000원

## 디버깅용 출력 (=) (파이썬 3.8 이상)
# 변수에 무슨 값이 들어있는지 빠르게 확인(디버깅)하고 싶을 때,
# 괄호 안에 변수명과 함께 =를 붙이면 '변수명=값' 형태로 알아서 출력
user_count = 125
print(f&quot;{user_count=}&quot;)
# =&amp;gt; 출력: user_count=125
## 정렬과 공백 채우기 (Alignment)
# 텍스트를 좌측, 우측, 또는 가운데로 정렬하고 
# 남는 공간을 특정 문자로 채울수 있다
'''
:&amp;lt;[길이]: 좌측 정렬
:&amp;gt;[길이]: 우측 정렬
:^[길이]: 가운데 정렬
'''
text = &quot;당목담글&quot;
# 10칸의 공간을 만들고 정렬
print(f&quot;|{text:&amp;lt;10}|&quot;) # 좌측 정렬 -&amp;gt; |당목담글      |
print(f&quot;|{text:&amp;gt;10}|&quot;) # 우측 정렬 -&amp;gt; |      당목담글|
print(f&quot;|{text:^10}|&quot;) # 가운데 정렬 -&amp;gt; |   당목담글   |

## 빈 곳을 원하는 문자로 채우기
# 방향 표시(&amp;lt;, &amp;gt;, ^) 앞에 원하는 문자를 넣으면, 
# 공백 대신 그 문자로 빈 곳을 채운다
# 예: 가운데 정렬하고 남는 공간을 하이픈(-)으로 채우기
print(f&quot;{text:-^20}&quot;)
# =&amp;gt; 출력: --------당목담글--------

## 날짜 및 시간(datetime) 포맷팅
# datetime 모듈을 사용할 때, 번거롭게 .strftime() 메서드를 쓰지 않고 
# f-string 안에서 곧바로 원하는 날짜/시간 형식으로 출력할 수 있다
# 연-월-일 시:분:초 형식으로 출력
from datetime import datetime
now = datetime.now() # now() 현재 시스템의 시간 추출 함수
print(f&quot;현재 시간: {now:%Y-%m-%d %H:%M:%S}&quot;)
# =&amp;gt; 출력: 현재 시간: 2026-07-09 09:41:01

## 중괄호 {} 문자 자체를 출력
# 중괄호를 두 번 연속({{, }}) 쓰면 출력 가능
group = &quot;릴레이&quot;
print(f&quot;플랫폼 구성 요소: {{{group}}}&quot;)
# =&amp;gt; 플랫폼 구성 요소: {릴레이}

##  따옴표가 겹치는 상황 해결
# =&amp;gt; f-string 내부에서 딕셔너리(Dictionary)의 키를 조회할 때 자주 발생
# =&amp;gt; f-string 바깥쪽 따옴표와 안쪽 따옴표를 
#     서로 다른 종류(쌍따옴표/홑따옴표)로 사용
user = {&quot;name&quot;: &quot;김철수&quot;, &quot;role&quot;: &quot;페르소나&quot;}
print(f&quot;사용자 역할: {user['role']}&quot;) # 사용자 역할: 페르소나

# 에러 예
print(f&quot;사용자 역할: {user[&quot;role&quot;]}&quot;) # SyntextError 발생

## 진수(Binary, Hex) 변환 및 0 채우기
# =&amp;gt; 숫자를 2진수, 8진수, 16진수로 바꾸거나, 
#    지정한 길이만큼 앞자리를 0으로 채워야 할 때
number = 42
print(f&quot;2진수: {number:b}&quot;) # 2진수: 101010
print(f&quot;16진수: {number:x}&quot;) # 16진수: 2a

# 빈 자리는 0으로 채우기
page = 7
print(f&quot;페이지 번호: {page:03d}&quot;) # 페이지 번호: 007&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/4</guid>
      <comments>https://bb0ng2.tistory.com/4#entry4comment</comments>
      <pubDate>Thu, 9 Jul 2026 16:36:45 +0900</pubDate>
    </item>
    <item>
      <title>0708 데이터 분석 및 시각화</title>
      <link>https://bb0ng2.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 다루기 위해서는 데이터를 가공하고 분석만 하는데 그쳐선 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 한 눈에 볼 수 있게시각화하는 과정이 반드시 필요하다. 가공되지 않은 데이터의 이상치를 찾아내거나,&amp;nbsp; 결측치, 이상치, 데이터 왜곡 및 타입 오류를 해결하여 의미있는 데이터로 만들고 의미있는 인사이트를 도출해낼 때 데이터의 시각화를 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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: #ffffff;&quot;&gt;(&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;를 활용해서 지역별 데이터의 평균을 분석하고 시각화 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 데이터 로드&lt;/p&gt;
&lt;pre id=&quot;code_1783604165455&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd

happy_life = pd.read_excel('./data/대한민국행복지도_삶의만족도.xlsx')
happy_life.head();
happy_health =pd.read_excel('./data/대한민국행복지도_건강.xlsx')
happy_health.head()
happy_econo = pd.read_excel('./data/대한민국행복지도_경제.xlsx')
happy_econo.head()
happy_relation =pd.read_excel('./data/대한민국행복지도_관계및사회참여.xlsx')
happy_relation.head()
happy_edu = pd.read_excel('./data/대한민국행복지도_교육.xlsx')
happy_edu.head()
happy_safe = pd.read_excel('./data/대한민국행복지도_안전.xlsx')
happy_safe.head()
happy_leisure = pd.read_excel('./data/대한민국행복지도_여가.xlsx')
happy_leisure.head()
happy_environ = pd.read_excel('./data/대한민국행복지도_환경.xlsx')
happy_environ.head()&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;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;데이터묶음&lt;/span&gt; 들을 pandas의 read_excel 함수로 읽어온다.&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 id=&quot;code_1783604238775&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;city = list(happy_life['시도'].unique())# 중복제거

happy_merge = pd.DataFrame({'시도' : city})
#삶의 만족도
life = happy_life['삶의 만족도'].groupby(by=happy_health['시도']).mean()
happy_merge = pd.merge(happy_merge,life,on='시도')
#건강
health = happy_health['평균'].groupby(by=happy_health['시도']).mean()
happy_merge = pd.merge(happy_merge,health.rename('건강'),on='시도')
#경제
econo = happy_econo['평균'].groupby(by=happy_econo['시도']).mean()
happy_merge = pd.merge(happy_merge,econo.rename('경제'),on='시도')
#관계및사회참여
relation = happy_relation['평균'].groupby(by=happy_relation['시도']).mean()
happy_merge = pd.merge(happy_merge,relation.rename('관계및사회참여'),on='시도')
#교육
edu = happy_edu['평균'].groupby(by=happy_edu['시도']).mean()
happy_merge = pd.merge(happy_merge,edu.rename('교육'),on='시도')
#안전
safe = happy_safe['평균'].groupby(by=happy_safe['시도']).mean()
happy_merge = pd.merge(happy_merge,safe.rename('안전'),on='시도')
#환경
environ = happy_environ['평균'].groupby(by=happy_environ['시도']).mean()
happy_merge = pd.merge(happy_merge,environ.rename('환경'),on='시도')
#여가
leisure = happy_leisure['평균'].groupby(by=happy_leisure['시도']).mean()
happy_merge = pd.merge(happy_merge,leisure.rename('여가'),on='시도')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 &lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;데이터묶음&lt;/span&gt; 의 '시도' 데이터들을 unique() 함수를 통해 중복이 없는 시도집합을 city 리스트에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복이 없는시도 데이터들을값으로 갖고 '시도'라는 칼럼을 갖는 happy_merge를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 &lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;데이터묶음&lt;/span&gt; 의 '시도' 데이터들로 그룹화 하여 데이터의 평균을 값으로 갖는 시리즈들을 만들고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 공부했던 pandas의 merge 함수를 이용해 각 데이터프레임의 '시도' 데이터로 happy_merge와 병합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 각 &lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;데이터묶음&lt;/span&gt; 은 같은 종류의 시도를 갖기 때문에 새로운 결측치가 생기지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#데이터 분석&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783605470131&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;happy_merge.info()
happy_merge.describe()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 info(), describe()를 통해 결측치를 알아보고 평균을 알아본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b646UQ/dJMcaassvvO/5d7frqPXV7dcOvrfPkN0uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b646UQ/dJMcaassvvO/5d7frqPXV7dcOvrfPkN0uK/img.png&quot; data-alt=&quot;결측치는 없군.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b646UQ/dJMcaassvvO/5d7frqPXV7dcOvrfPkN0uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb646UQ%2FdJMcaassvvO%2F5d7frqPXV7dcOvrfPkN0uK%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;353&quot; height=&quot;290&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결측치는 없군.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn6Ekf/dJMb991oaxZ/IAMbBHZgwwK1b8ZjnzxRt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn6Ekf/dJMb991oaxZ/IAMbBHZgwwK1b8ZjnzxRt0/img.png&quot; data-alt=&quot;칼럼별 개수, 평균, 표준편차, 최저값,최고값 등&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn6Ekf/dJMb991oaxZ/IAMbBHZgwwK1b8ZjnzxRt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn6Ekf%2FdJMb991oaxZ%2FIAMbBHZgwwK1b8ZjnzxRt0%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;644&quot; height=&quot;255&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;칼럼별 개수, 평균, 표준편차, 최저값,최고값 등&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1783605598576&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;happy_merge.mean(axis=1,numeric_only=True)# 행의 평균
happy_merge.mean(axis=0,numeric_only=True)# 열의 평균&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 행과 열의 평균을 알아본다. axis =1 : 행 axis = 0 : 열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시도문자열(서울특별시,경기도 등)이 인덱스가 아니고 값이기 때문에, 숫자의 평균을 내는데 문자열 데이터가 섞여들어가는 꼴이기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;mean함수를 사용할때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;numeric_only=True를 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783606314528&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;happy_merge.set_index(['시도']).mean(axis=1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;('시도' 데이터를 값 -&amp;gt; 인덱스로 바꾼 데이터프레임).mean이기때문에 여기선 numeric_only=True이 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783606394092&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;happy_merge.corr(numeric_only=True) # 각 변수(컬럼)들 간의 상관 관계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;corr은 각 칼럼들의 상관관계를 나타내는 함수이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zNyKG/dJMcafAvVaK/QoF0LMkMD3mlV9EKkLKsH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zNyKG/dJMcafAvVaK/QoF0LMkMD3mlV9EKkLKsH0/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNyKG/dJMcafAvVaK/QoF0LMkMD3mlV9EKkLKsH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNyKG%2FdJMcafAvVaK%2FQoF0LMkMD3mlV9EKkLKsH0%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;619&quot; height=&quot;160&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;p data-ke-size=&quot;size16&quot;&gt;#데이터 시각화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 데이터를 시각화할때가 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 시각화를 위해서는 matplotlib와 seaborn 라이브러리가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783607101255&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import seaborn as sns

#OS에 따른 폰트 설정방법
path = 'c:/Windows/Fonts/malgun.ttf'
import platform
from matplotlib import font_manager, rc
if platform.system()=='Darwin':
    rc('font',family='AppleGothic')
elif platform.system() =='Windows':
    font_name=font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~')&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783607230194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plt.figure(figsize=(15,6))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플롯의 크기 를 설정한다. 가로 15, 세로 6&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783607319027&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;item = list(happy_merge.columns[1:9])
for a in item:
    chartdata = happy_merge[a]
    plt.plot(chartdata, marker='o', label=a)&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;칼럼의 0열은 '시도' 이므로 1열~8열까지 저장하는 리스트 item을 만들고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;item의 값을 인덱스로하는 happy_merge의 행을&amp;nbsp; chartdata에 저장하고, plt.plot 함수로 가로축에 지역 라벨을 붙이고 o 모양의 점을 찍고 선을 이어 그래프를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VZic6/dJMcafUQjfS/Lt3d6BMXM1HOBG5v5upg90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VZic6/dJMcafUQjfS/Lt3d6BMXM1HOBG5v5upg90/img.png&quot; data-alt=&quot;플롯 그리기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VZic6/dJMcafUQjfS/Lt3d6BMXM1HOBG5v5upg90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVZic6%2FdJMcafUQjfS%2FLt3d6BMXM1HOBG5v5upg90%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;593&quot; height=&quot;255&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플롯 그리기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지만 하면 뭔가 심심하다. 그래프의 제목과, 가로축 세로축 라벨, 격자, 범례를 추가해주자.&lt;/p&gt;
&lt;pre id=&quot;code_1783608631092&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plt.xlabel('시도')
plt.ylabel('수치')
plt.xticks(range(17), happy_merge.index,fontsize=10,rotation=45)
plt.title('대한민국 행복지수')
plt.legend()
plt.grid()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2jiXq/dJMcacjqnwU/HlnMRS3UW7i9TiD2cGno51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2jiXq/dJMcacjqnwU/HlnMRS3UW7i9TiD2cGno51/img.png&quot; data-alt=&quot;짜잔 좀더 이쁜그래프 완성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2jiXq/dJMcacjqnwU/HlnMRS3UW7i9TiD2cGno51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2jiXq%2FdJMcacjqnwU%2FHlnMRS3UW7i9TiD2cGno51%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;590&quot; height=&quot;281&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;짜잔 좀더 이쁜그래프 완성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#막대 그래프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783609276594&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from numpy import *

happy_merge.plot(kind='bar', xlabel = '시도', ylabel='수치',
                 figsize=(15,20), grid=True, subplots=True)

plt.suptitle('대한민국 행복지수', fontsize=25)
plt.tight_layout(pad=8, h_pad =2)
plt.show()&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;위 코드는 matplotlib을 사용하지 않고 pandas의 내장 함수 plot()을 사용해서 막대그래프를 출력하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;subplots=True을 통해 칼럼별로 그래프를 나눠서 그릴 수 있었다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czeHir/dJMcahLP6nO/omD3en01v2k0KwLiY3vKL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czeHir/dJMcahLP6nO/omD3en01v2k0KwLiY3vKL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czeHir/dJMcahLP6nO/omD3en01v2k0KwLiY3vKL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczeHir%2FdJMcahLP6nO%2FomD3en01v2k0KwLiY3vKL0%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;449&quot; height=&quot;639&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꺾은선 그래프와 달리 칼럼 수 슬라이스 처리를 하지 않아 빈 그래프가 하나 더추가된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#히트 맵&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783610167818&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import seaborn as sns

plt.figure(figsize=(15,13))
plt.title('대한민국 행복지수 상관관계',fontsize=25)
plt.rc('axes', unicode_minus=False)
#unicode_minus =False : 한글 사용시, 가끔 '-'부호가 꺠지는 것을 방지
correlation_mat = happy_merge.corr(numeric_only=True)
#happy_merge.corr() : happy_merge의 모든 컬럼들에 대한 
#                     상관관계를 계산하여 계수 표를 만듦
# numeric_only = True : 숫자 영역만
upp_mat = triu(correlation_mat)
#np.triu() : 상관관계 표는 대각선을 기본으로 거울 처럼 표현
#np.ones_like(dtype=bool) :상관관계표와 동일한 크기로 채워진 표
# correlation_mat : corr()로 구한 상관관계 표 데이터
# 참고: triu는 TRIANGLE UPPER의 약자
sns.heatmap(correlation_mat, mask=upp_mat,
            annot=True, cmap='RdYlGn', fmt= '.2f')
# mask=upp_mat : 히트맵의 오른쪽 상단을 가림(masking)
# annot=True : 색상 위에 실제 상관계수를 쓰기
# cmap = 'RdYlGn' : 제공하는 색상 팔레트를 사용 Red Yellow Green
# red-&amp;gt;yellow-&amp;gt;green 양의 상관관계가 높으면 green
# 음의 상관관계가 높으면 red
# fmt= '.2f' : 숫자를 소수점 이하 두자리까지만

plt.show()&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cElmdQ/dJMcaiqq4uy/rGdqqOIFAnAcA6Iw72x3n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cElmdQ/dJMcaiqq4uy/rGdqqOIFAnAcA6Iw72x3n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cElmdQ/dJMcaiqq4uy/rGdqqOIFAnAcA6Iw72x3n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcElmdQ%2FdJMcaiqq4uy%2FrGdqqOIFAnAcA6Iw72x3n1%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;666&quot; height=&quot;641&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;이상치&amp;nbsp;확인을&amp;nbsp;위한&amp;nbsp;boxplot의&amp;nbsp;예&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783610803076&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

#한글 깨짐 방지 설정 (Windows: 'Malgun Gothic', Mac : 'AppleGothic')
plt.rc('font', family='Malgun Gothic')

#1 예시 데이터 만들기(정상 기온 20도 안팎 + 이상치 몇 개)
data = {
        '연도': [2026]*100,
        '기온': list(np.random.normal(loc=20,scale=3,size=97))+[45.2,-15.0,41.8]
        # 45도, -15도 등의 이상치 삽입
        }
weather =pd.DataFrame(data)

#2 boxplot 그리기
plt.figure(figsize=(6,8))
sns.boxplot(y='기온',data=weather,
            color='skyblue',
            flierprops={'markerfacecolor':'red', 'marker':'o'})

#flierprops={'markerfacecolor':'red','marker':'o'}
'''
이상치(Outlier) 점들의 스타일(색상, 모양 등)을 내 입맛대로 꾸미기 위해 사용하는 옵션
'''
plt.title('기온 데이터의 이상치 분석')
plt.ylabel('기온(℃)')
plt.show()
'''
boxplot 보는 방법
'''&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;임의의 데이터 만들기 : 정상치 데이터 97개 + 이상치 데이터 3개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb97Vt/dJMcaalBgtZ/s6kcJYQ7liRrhUBAYfR6u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb97Vt/dJMcaalBgtZ/s6kcJYQ7liRrhUBAYfR6u1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb97Vt/dJMcaalBgtZ/s6kcJYQ7liRrhUBAYfR6u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb97Vt%2FdJMcaalBgtZ%2Fs6kcJYQ7liRrhUBAYfR6u1%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;519&quot; height=&quot;643&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 박스플롯그래프를 보면 하늘색 박스가평균, 빨간 마커가 이상치로 한눈에 파악할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;상관계수 용어 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783610646007&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
 ...
 1~0~-1 사이의 숫자로 표현
1: 매우 밀접한 관계 : 하나가 크면 다른 하나도 증가 예) 키와 몸무게, 공부 시간과 성적
0: 관계가 없다
-1:매우 밀접한 관계 : 하나가 크면 다른 하나는 감소
    예) 스마트 폰 사용시간과 수면시간

즉, 1, -1은 두 데이터가 강력하게 묶여 있다는 의미    
    0은 두 데이터는 상관 없다
 공분산(Covariance) : 두 개의 데이터가 함께 변화하는 '방향'을 나타내는 통계 값
 공분산이 알려주는 3가지 방향
 공분산&amp;gt; 0 (양수): $X$가 커질 때 $Y$도 같이 커지는 경향이 있다. (우상향)
 공문산&amp;lt; 0 (음수): $X$가 커질 때 $Y$는 작아지는 경향이 있다. (우하향) 공분산 = 0: 두 데이터 사이에 뚜렷한 직선 형태의 관계가 없다.
 두 데이터 X와 Y 있을 때,
 공분산 값의 부호를 보면 두 데이터가 같이 움직이는지, 반대로 움직이는지 알 수 있다 공분산은 두 데이터가 '어떤 방향'으로 움직이는지는 확실하게 알려주지만,
 &quot;그 관계가 얼마나 강한지&quot;는 제대로 알려주지 못한다.
 사람의 '키'와 몸무게'의 공분산을 구한다고 가정.
 키를 미터(m)로 재서 계산했을 때 공분산이 1.5가 나왔다.
 똑같은 사람들의 키를 밀리미터(mm)로 바꿔서 계산하면, 숫자의 단위가 커졌기 때문에 공분산 값이 감자기 1500으로 폭증.
 데이터의 실제 관계는 똑같은데 단위에 따라 숫자가 1.5에서 1500으로 바뀌어 버리니, 다른 데이터와 비교하기가 불가능해진다.
 그래서 이 공분산 을 각 데이터의 '표준편차'로 나누어서, 측정 단위에 상관없이 항상 1에서 그 사이의 값만 나오도록 꽉 암축(정규화)한 것이 바로 앞서 살펴본 상관계수(r).
 &amp;bull;..
 &amp;bull;..
 표준편차 : 데이터들이 평균으로부터 얼마나 멀리 떨어져서 흩어져 있는가?&quot;를 나타내는 숫자
표준편차가 알려주는 것
표준편차가 작다: 데이터들이 평균 근처에 오밀조일하게 모여 있다는 뜻.
(안정적, 예측 가능)
표준편차가 크다: 데이터들이 평균에서 멀리 떨어져서 밟게 퍼져 있다는 뜻.
(변동성이 큼, 들쭉날쭉함)
4반과 B반의 수학 시험 평균이 둘다 70점이라고 가정.
A반: 대부분의 학생이 65점~ 75점 사이를 받았다.    
(평균 근처에 몰려 있음 -&amp;gt; 표준편차가 작다!)
B반: 100점 맞은 학생도 있고, 40점 맞은 학생도 섞여 있다.
(점수가 널뛰기함&amp;mdash;&amp;gt; 표준편차가 크다!)
즉, 평균만으로는 알 수 없는 '데이터의 진짜 생김새(퍼짐 정도)'를 알려주는 것이 바로 표준편차
'''

'''
np.random.multivariate_normal(mean, cov, num_points).T

np.random : 임의의 수를 추출
multivariate_normal() : Numpy가 제공하는 다변량 정규분포를 만들어 주는 함수

다변량(Multivariate): 변수가 여러 개(여기서는 X와 Y, 2개) 라는 뜻.
mean (평균) 점들이 어디에 모여 있을지 '중심점'을 정해주기 위함.
코드에서는 [0,0]이었으니 영점(원점) 근처에 점들이 위치.
cov (공분산 행렬): X와 Y가 서로 어떤 방향으로, 얼마나 강하게 엮여서 흩어질지 '규'
여기에 우리가 조절했던 상관계수 (r) 값이 들어간다.
.num_points (개수): 점을 몇 개 찍을 지 정한다. (예: 200개)

따라서 위 함수가 실행되면
파이썬은 (200, 2) 모양의 행렬 (배열)을 만들어낸다.
즉, 200줄(행) 2칸(열)짜리 표가 만들어진다.

.T (Transpose, 전치행렬) : 행과 열을 바꾸는 역할.
데이터가 [x,y] 쌍으로 묶여서 200줄로 서 있으면, 
나중에 x축 데이터와 y축 데이터를 따로 분리해서 그래프에 넣기가 번거롭다.

그래서 수학의 '전치행렬(행과 열을 뒤바꿈' 기능을 하는 .T를 뒤에 붙여준다.
이렇게 하면 (200,2) 모양의 데이터가 (2,200) 모양으로 확 뒤집힌다.
'''&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/3</guid>
      <comments>https://bb0ng2.tistory.com/3#entry3comment</comments>
      <pubDate>Thu, 9 Jul 2026 00:31:36 +0900</pubDate>
    </item>
    <item>
      <title>0707 Pandas</title>
      <link>https://bb0ng2.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Pandas&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span data-newtext-seq=&quot;8&quot;&gt;파이썬에서 &lt;/span&gt;&lt;b&gt;&lt;span data-newtext-seq=&quot;20&quot;&gt;표(테이블) 형태의 데이터를 다루기 위한 라이브러리&lt;/span&gt;&lt;/b&gt;&lt;span data-newtext-seq=&quot;20&quot;&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pandas에서 가장 알아야 할 것은 Seriese, DataFrame이라는 자료구조이다. 1차원 데이터를 다루는 데 효과적인 Seriese와 2차원 데이터를 다루는 데 효과적인DataFrame을 활용하여 파이썬만으로는 할 수 없었던&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; 데이터 정제, 데이터 가공, 데이터 요약 및&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;분석&lt;/span&gt;&lt;/b&gt;을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#지정한 문자열 인덱스로 데이터 접근&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783522808766&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kakao2 = Series([92600,92400,92100,94300,92300],
                index=['2016-02-19',
                       '2016-02-18',
                       '2016-02-17',
                       '2016-02-16',
                       '2016-02-15'])

print(kakao2['2016-02-19'])&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;시리즈를 생성할때 지정한 인덱스(2016-02-19)를 통해 데이터에 접근한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;딕셔너리를&amp;nbsp;사용한&amp;nbsp;기본&amp;nbsp;DataFrame&amp;nbsp;생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;칼럼&amp;nbsp;선택을&amp;nbsp;통해&amp;nbsp;각&amp;nbsp;Series&amp;nbsp;객체에&amp;nbsp;접근&lt;/p&gt;
&lt;pre id=&quot;code_1783523123052&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;raw_data = {'col0': [1,2,3,4],
            'col1': [10,20,30,40],
            'col2': [100,200,300,400],}


# 칼럼 선택을 통해 각 Series 객체에 접근
data = DataFrame(raw_data)
data['col1']
print(data['col0'])
print(type(data['col0']))&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;key:string value : list(int)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인 딕셔너리 raw_data를 사용해서 data라는 DataFrame을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key값이었던 문자열들이 칼럼이 되어 data['col1']처럼 칼럼 선택을 통해 각 Series 객체에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#인덱스&amp;nbsp;순서가&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;Series&amp;nbsp;생성&lt;/p&gt;
&lt;pre id=&quot;code_1783523490063&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mine = Series([10,20,30],index=['naver','sk','kt'])
friend = Series([10,30,20],index=['kt','naver','sk'])

merge = mine + friend&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lxYlR/dJMcaftHBok/lAJjvAV0tp14bhp5gqWXFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lxYlR/dJMcaftHBok/lAJjvAV0tp14bhp5gqWXFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lxYlR/dJMcaftHBok/lAJjvAV0tp14bhp5gqWXFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlxYlR%2FdJMcaftHBok%2FlAJjvAV0tp14bhp5gqWXFk%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;231&quot; height=&quot;200&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;데이터(OHLC)&amp;nbsp;DataFrame&amp;nbsp;생성&amp;nbsp;(칼럼&amp;nbsp;순서&amp;nbsp;및&amp;nbsp;인덱스&amp;nbsp;지정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783594805846&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#주가 데이터(OHLC) DataFrame 생성 (칼럼 순서 및 인덱스 지정)
daeshin = {'open': [11650,11100,11200,11100,11000],
           'high': [12100,11800,11200,11100,11150],
           'low': [11600,11500,10900,10950,10900],
           'close': [11900,11600,11000,11100,11050]
           }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신증권의 시가, 고가, 저가, 종가 문자열을 키, 가격을 값으로 갖는 딕셔너리를 정의한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;columns로&amp;nbsp;칼럼&amp;nbsp;순서를&amp;nbsp;정렬하고, &lt;br /&gt;#&amp;nbsp;index로&amp;nbsp;날짜를&amp;nbsp;지정하여&amp;nbsp;생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783595082613&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;daeshin_day = DataFrame(daeshin,
                        columns=['open', 'low', 'high','close'])

# index로 날짜를 지정하여 생성
date = ['16.02.29', '16.02.26', '16.02.25', 
        '16.02.24', '16.02.23']
daeshin_day = DataFrame(daeshin,
                        columns=['open',
                                 'high',
                                 'low',
                                 'close'],
                        index=date)&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;앞서 정의한 daeshin 딕셔너리와 다시 정의한 칼럼 순서, 리스트로 정의한 날짜 date를 인덱스로 만들어 DataFrame()에 전달해 데이터프레임을 만든다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 칼럼 선택 (종가 데이터 선택)&lt;/p&gt;
&lt;pre id=&quot;code_1783595610238&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;close = daeshin_day['close']
print(close)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터프레임 daeshin_day에서 'close' 칼럼의 정보를 날짜별로 모아놓은 close라는 시리즈를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;131&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQEZgh/dJMcahrBdIo/ANPZkfmrDagjKAq8VGlTz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQEZgh/dJMcahrBdIo/ANPZkfmrDagjKAq8VGlTz0/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQEZgh/dJMcahrBdIo/ANPZkfmrDagjKAq8VGlTz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQEZgh%2FdJMcahrBdIo%2FANPZkfmrDagjKAq8VGlTz0%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;131&quot; height=&quot;82&quot; data-origin-width=&quot;131&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;#&amp;nbsp;로우&amp;nbsp;선택&lt;/p&gt;
&lt;pre id=&quot;code_1783595764025&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# (기존 ix 대신 loc 메서드를 사용하여 특정 날짜 행 선택)
print(daeshin_day.loc['16.02.24'])&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;close와 달리 인덱스를 사용해 해당 날짜의 시가, 고가, 저가, 종가를 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;63&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjC2PX/dJMcadP9fTM/UCbdQ1LXUs5NTlYf31O4Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjC2PX/dJMcadP9fTM/UCbdQ1LXUs5NTlYf31O4Dk/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjC2PX/dJMcadP9fTM/UCbdQ1LXUs5NTlYf31O4Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjC2PX%2FdJMcadP9fTM%2FUCbdQ1LXUs5NTlYf31O4Dk%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;132&quot; height=&quot;63&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;63&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;# 방법 A : .loc[행, 열] 구조로 정확하게 짚기&lt;/p&gt;
&lt;pre id=&quot;code_1783596031290&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(daeshin_day.loc['16.02.24', 'close'])&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;데이터프레임 daeshin_day에서 '16.02.24' 행 'close'열을 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;16&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nTs6H/dJMcahZlHAT/MLhSkG17OpeLKYfWwoTH60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nTs6H/dJMcahZlHAT/MLhSkG17OpeLKYfWwoTH60/img.png&quot; data-alt=&quot;출력결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nTs6H/dJMcahZlHAT/MLhSkG17OpeLKYfWwoTH60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnTs6H%2FdJMcahZlHAT%2FMLhSkG17OpeLKYfWwoTH60%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;53&quot; height=&quot;16&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;16&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;방법&amp;nbsp;B:&amp;nbsp;이미&amp;nbsp;뽑아둔&amp;nbsp;close&amp;nbsp;&amp;nbsp;시리즈에서&amp;nbsp;날짜로&amp;nbsp;조회하기&lt;/p&gt;
&lt;pre id=&quot;code_1783596203529&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(close['16.02.24'])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리 정의한 close 시리즈에 인덱스를 넣어 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;16&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9bCY1/dJMcafm1eJx/HN2thDo7KFlZT5XhYJBnt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9bCY1/dJMcafm1eJx/HN2thDo7KFlZT5XhYJBnt0/img.png&quot; data-alt=&quot;출력결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9bCY1/dJMcafm1eJx/HN2thDo7KFlZT5XhYJBnt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9bCY1%2FdJMcafm1eJx%2FHN2thDo7KFlZT5XhYJBnt0%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;53&quot; height=&quot;16&quot; data-origin-width=&quot;53&quot; data-origin-height=&quot;16&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#CSV&amp;nbsp;파일&amp;nbsp;불러오기&lt;/p&gt;
&lt;pre id=&quot;code_1783596280302&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd

df = pd.read_csv('./data/csv/medical.csv')&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;pandas를 import 해주고 read_csv 함수를 호출 해 medical.csv 파일을 읽어와 데이터프레임으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(필요시 인코딩방식 명시)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;데이터의&amp;nbsp;상위&amp;nbsp;,&amp;nbsp;하위&amp;nbsp;5개&amp;nbsp;행&amp;nbsp;확인&lt;/p&gt;
&lt;pre id=&quot;code_1783596349853&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(df.head())
print(df.tail())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터프레임의 상위 , 하위 5개 행을 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB98mS/dJMcajiBjt5/hVXlqU31bJEvbPqiKzN5Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB98mS/dJMcajiBjt5/hVXlqU31bJEvbPqiKzN5Z0/img.png&quot; data-alt=&quot;출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB98mS/dJMcajiBjt5/hVXlqU31bJEvbPqiKzN5Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB98mS%2FdJMcajiBjt5%2FhVXlqU31bJEvbPqiKzN5Z0%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;624&quot; height=&quot;238&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#데이터의&amp;nbsp;기본&amp;nbsp;정보&amp;nbsp;(행/열&amp;nbsp;개수,&amp;nbsp;데이터&amp;nbsp;타입,&amp;nbsp;결측치&amp;nbsp;유무)&amp;nbsp;확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;기술&amp;nbsp;통계량(평균,&amp;nbsp;최댓값,&amp;nbsp;최솟값&amp;nbsp;등&amp;nbsp;)&amp;nbsp;요약&amp;nbsp;확인&lt;/p&gt;
&lt;pre id=&quot;code_1783596446392&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(df.info())
print(df.describe())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 행/열 개수, 데이터 타입, 결측치 유무를 확일 할 수 있는 info() 함수, 기술 통계량(평균, 최댓값, 최솟값 등 ) 요약을 확인할 수 있는 describe() 함수이다. 추가로 각 컬럼별 결측치의 개수를 확인 할 수 있는 함수를 isnull().sum()이라고한다. 위 함수들은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 정제 과정에서 꼭 필요한 함수들이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;'Age'&amp;nbsp;컬럼이&amp;nbsp;60&amp;nbsp;이상인&amp;nbsp;데이터만&amp;nbsp;필터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783596766264&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;senior_patients = df[df['Age']&amp;gt;=60]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Age 컬럼의 열 데이터 중 60이상인데이터만 필터링 하여 senior_patients(dtype : Series)에 저장한다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;여러&amp;nbsp;조건&amp;nbsp;결합&amp;nbsp;(나이가&amp;nbsp;60세&amp;nbsp;이상이면서&amp;nbsp;'NO_SHOW'가&amp;nbsp;'Yes'인&amp;nbsp;환자)&lt;/p&gt;
&lt;pre id=&quot;code_1783596862958&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;no_show_seniors =df[(df['Age']&amp;gt;=60)&amp;amp;(df['No-show']=='Yes')]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리연산자 &amp;amp;를 사용하여 Age가 60 이상, No-show가 Yes인 데이터만 필터링하여 no_show_seniors( dtype : Series )에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장한다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치 처리 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;방법&amp;nbsp;A&amp;nbsp;:&amp;nbsp;결측치가&amp;nbsp;포함된&amp;nbsp;행을&amp;nbsp;모두&amp;nbsp;삭제하기&lt;/p&gt;
&lt;pre id=&quot;code_1783596973298&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df_cleaned =df.dropna()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;# 방법 B : 결측치를 특정 값(예: 0 또는 평균값)으로 채우기&lt;/p&gt;
&lt;pre id=&quot;code_1783596980512&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df_filled =df.fillna(0)
df['Age'] = df[&quot;Age&quot;].fillna(df['Age'].mean())&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;###&amp;nbsp;4.&amp;nbsp;데이터&amp;nbsp;그룹화&amp;nbsp;및&amp;nbsp;집계&amp;nbsp;(groupby)####&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;br /&gt;#&amp;nbsp;'No-show'(노쇼&amp;nbsp;여부)별로&amp;nbsp;'Age'(나이)의&amp;nbsp;평균&amp;nbsp;계산하기&lt;/p&gt;
&lt;pre id=&quot;code_1783597115514&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;age_by_noshow = df.groupby('No-show')['Age'].mean()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;age_by_noshow는 노쇼 여부별로 Age의 평균값을 데이터로 갖는 시리즈이다.&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;여러&amp;nbsp;컬럼을&amp;nbsp;기준으로&amp;nbsp;집계&amp;nbsp;(성별과&amp;nbsp;노쇼&amp;nbsp;여부별&amp;nbsp;환자&amp;nbsp;수&amp;nbsp;카운트)&lt;/p&gt;
&lt;pre id=&quot;code_1783597125664&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;grouped_count = df.groupby(['Gender', 'No-show']).size()
print(grouped_count.index)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grouped_count는 df에서 Gender, No-show의 종류 (여기서는 개수는 2*2) 별로 환자수를 센다.&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;'Age'&amp;nbsp;컬럼을&amp;nbsp;기준으로&amp;nbsp;내림차순&amp;nbsp;정렬&amp;nbsp;(나이가)&lt;/p&gt;
&lt;pre id=&quot;code_1783597144111&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df_sorted = df.sort_values(by='Age', ascending =False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;df에서 Age칼럼의값을 기준으로 내림차순 정렬한다. ascending=True면 오름차순.&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;# 'No-show' 컬럼에 어떤 값이 각각 몇개씩 들어있는지 확인&lt;/p&gt;
&lt;pre id=&quot;code_1783597165673&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(df['No-show'].value_counts())&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;br /&gt;#가장&amp;nbsp;첫&amp;nbsp;번째&amp;nbsp;행(0번행)&amp;nbsp;데이터&amp;nbsp;가져오기&lt;/p&gt;
&lt;pre id=&quot;code_1783597178877&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;first_row =df.iloc[0]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;df의 0번 행을 저장한다.&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;처음&amp;nbsp;5개&amp;nbsp;행과&amp;nbsp;처음&amp;nbsp;3개&amp;nbsp;열&amp;nbsp;슬라이싱&amp;nbsp;하기&lt;/p&gt;
&lt;pre id=&quot;code_1783597196246&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;subset = df.iloc[0:5, 0:3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0~4번째 행, 0~2번째 열 정보를 subset에 저장한다. (5행3열)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;#####&amp;nbsp;Pandas&amp;nbsp;pivot_table&amp;nbsp;사용법&amp;nbsp;##### &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;index:&amp;nbsp;세로&amp;nbsp;행에&amp;nbsp;배치할&amp;nbsp;기준 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;columns:&amp;nbsp;가로&amp;nbsp;열에&amp;nbsp;배치할&amp;nbsp;기준 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;values:&amp;nbsp;표&amp;nbsp;안에&amp;nbsp;채워&amp;nbsp;넣을&amp;nbsp;데이터(계산&amp;nbsp;대상)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;aggfunc: 데이터의 대상 함수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;#&amp;nbsp;예시&amp;nbsp;1:&amp;nbsp;성별(Gender)과&amp;nbsp;노쇼&amp;nbsp;여부(No-show)에&amp;nbsp;따른&amp;nbsp;평균&amp;nbsp;나이&amp;nbsp;(Age) &lt;br /&gt;#&amp;nbsp;세로(index):&amp;nbsp;성별(Gender) &lt;br /&gt;#&amp;nbsp;가로(columns):&amp;nbsp;노쇼&amp;nbsp;여부&amp;nbsp;(No-show)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;값(values):&amp;nbsp;나이&amp;nbsp;(Age)의&amp;nbsp;평균&lt;/p&gt;
&lt;pre id=&quot;code_1783597713508&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pivot_age = df.pivot_table(index='Gender',
                           columns='No-show',
                           values='Age',
                           aggfunc='mean')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 'Gender'의 종류 (M,F), 칼럼을 'No-show'의 종류 (Yes, No), 데이터는 Age의 평균을 갖는 데이터프레임을 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;239&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nn31K/dJMcabSmqf4/rnbgW8wcI60QxbivVV6hh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nn31K/dJMcabSmqf4/rnbgW8wcI60QxbivVV6hh0/img.png&quot; data-alt=&quot;pivot_age 형태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nn31K/dJMcabSmqf4/rnbgW8wcI60QxbivVV6hh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnn31K%2FdJMcabSmqf4%2FrnbgW8wcI60QxbivVV6hh0%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;239&quot; height=&quot;83&quot; data-origin-width=&quot;239&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pivot_age 형태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;성별&amp;nbsp;및&amp;nbsp;노쇼&amp;nbsp;여부별&amp;nbsp;환자&amp;nbsp;수&amp;nbsp;카운트&lt;br /&gt;#&amp;nbsp;margins&amp;nbsp;인자&amp;nbsp;추가시&amp;nbsp;가로/세로&amp;nbsp;총합&amp;nbsp;추가&lt;/p&gt;
&lt;pre id=&quot;code_1783598697938&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pivot_gender_no_show = df.pivot_table(index='Gender',
                           columns='No-show',
                           values='PatientId',
                           aggfunc='count',
                           margins=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gender,Noshow 별 환자의 수를 구한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N7XGe/dJMcacjqkut/3kd1cYK3AyL00Bkwqt6VP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N7XGe/dJMcacjqkut/3kd1cYK3AyL00Bkwqt6VP1/img.png&quot; data-alt=&quot;pivot_gender_no_show 형태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N7XGe/dJMcacjqkut/3kd1cYK3AyL00Bkwqt6VP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN7XGe%2FdJMcacjqkut%2F3kd1cYK3AyL00Bkwqt6VP1%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;316&quot; height=&quot;120&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pivot_gender_no_show 형태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;#Pandas에는&amp;nbsp;df.pivot()과&amp;nbsp;df.pivot_table()&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;df.pivot():&amp;nbsp;데이터에&amp;nbsp;중복된&amp;nbsp;행이&amp;nbsp;없을&amp;nbsp;때 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;df.pivot_table():&amp;nbsp;병원&amp;nbsp;데이터처럼&amp;nbsp;중복된&amp;nbsp;&amp;nbsp;항목(같은&amp;nbsp;성별,&amp;nbsp;같은&amp;nbsp;노쇼&amp;nbsp;등) &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이&amp;nbsp;많아서&amp;nbsp;그룹화&amp;nbsp;하고&amp;nbsp;요약&amp;nbsp;통계(평균,&amp;nbsp;합계&amp;nbsp;등) &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;데이터&amp;nbsp;분석에서는&amp;nbsp;90%&amp;nbsp;이상&amp;nbsp;pivot_table을&amp;nbsp;사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;###&amp;nbsp;두&amp;nbsp;개&amp;nbsp;이상의&amp;nbsp;데이터프레임&amp;nbsp;병합하기&amp;nbsp;(merge) &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1783598829978&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 두 개 이상의 데이터프레임 병합하기 (merge)
# 예시 데이터프레임 1: 진료 기록
df_medical = pd.DataFrame({
    'PatientId': [101, 102, 103],
    'No-show':['No','Yes','No']
    })

# 예시 데이터프레임 2: 환자 주소 정보
df_info = pd.DataFrame({
    'PatientId' : [101,102,104],
    'Neighborhood' : ['Jardim', 'Maria', 'Centro']
    })

# PatientId를 기준으로 두 데이터 프레임 병합
df_merged = pd.merge(df_medical, df_info,
                     on='PatientId',
                     how='outer')
'''
1. how= 'inner' (기본값)
2. how='left'
3. how ='right'
4. how = 'outer'
'''&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;임의의 데이터프레임 df_medical(인덱스는 지정하지 않았으므로 기본인덱스0,1,2, 칼럼은 PatientId,No-show )과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;df_info( 인덱스는 지정하지 않았으므로 기본인덱스0,1,2, 칼럼은 PatientId, Neighborhood)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 데이터프레임이 공유하는 칼럼 PatientId를 활용하여 데이터프레임을 병합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막인자 how에 따라 어떤 데이터프레임의 PatientId값을 기준으로 병합할지 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inner 선택시 두 데이터프레임의 교집합인 PatientId를 기준으로 병합되므로 새로운 결측치가 발생하지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer 선택시 두 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;데이터프레임의 합집합인 PatientId를&lt;span&gt; 기준으로 병합되므로 새로운 결측치가 발생할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;###날짜&amp;nbsp;데이터&amp;nbsp;형식&amp;nbsp;변환(to_datetime)&amp;nbsp;###&lt;/p&gt;
&lt;pre id=&quot;code_1783599599678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 'AppointmentDay' 컬럼을 문자열에서 날짜 형식으로 변환
df['AppointmentDay'] = pd.to_datetime(df['AppointmentDay'])
df['ScheduledDay'] = pd.to_datetime(df['ScheduledDay'])

#2. 접수 후 예약일까지 며칠이나 걸렸는지 대기 기간 계산
df['Waiting_Days'] = (df['AppointmentDay'] - df['ScheduledDay']).dt.days&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;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;###&amp;nbsp;Pandas&amp;nbsp;데이터&amp;nbsp;분석&amp;nbsp;### &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;''' &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1.&amp;nbsp;데이터&amp;nbsp;로드&amp;nbsp;및&amp;nbsp;확인:&amp;nbsp;read_csv(),&amp;nbsp;head(),&amp;nbsp;info() &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2.&amp;nbsp;데이터&amp;nbsp;정제:&amp;nbsp;결측치&amp;nbsp;처리(dropna(),&amp;nbsp;fillna()),&amp;nbsp;날짜&amp;nbsp;변환(to_datetime()) &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3.&amp;nbsp;데이터&amp;nbsp;가공:&amp;nbsp;파생&amp;nbsp;변수&amp;nbsp;생성(apply()),&amp;nbsp;데이터&amp;nbsp;병합(merge()) &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;4.&amp;nbsp;데이터&amp;nbsp;요약&amp;nbsp;및&amp;nbsp;분석:&amp;nbsp;groupby(),&amp;nbsp;pivot_table(),&amp;nbsp;value_counts() &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;'''&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/2</guid>
      <comments>https://bb0ng2.tistory.com/2#entry2comment</comments>
      <pubDate>Thu, 9 Jul 2026 00:25:43 +0900</pubDate>
    </item>
    <item>
      <title>0706 Class</title>
      <link>https://bb0ng2.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체지향 프로그래밍의 핵심 문법으로 &lt;/span&gt;비슷한 속성(데이터)과 기능(함수)을 하나로 묶어서, 새로운 자료형(틀)을 만드는 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스로 생성한 객체들을 인스턴스라고 한다. 흔한 비유로 클래스를 정의하는 것은 붕어빵을 만들 때 사용하는 기본 틀을 제작하는 것에 해당하고, 인스턴스는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;붕어빵 틀에 반죽을 넣어서 만들어진 붕어빵에 해당한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++, 자바(Java), C# 같은 프로그래밍 언어는 기본적으로 클래스를 지원한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 클래스를 통해 명함이라는 객체를 만들어 두면 명함 객체가 정수, 실수, 문자열과 마찬가지로 하나의 타입으로 인식되기 때문에 여러 명의 명함 정보도 쉽게 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDqlQD/dJMcaccGcUi/6nQPxgMeDw3yTNFuNckKK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDqlQD/dJMcaccGcUi/6nQPxgMeDw3yTNFuNckKK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDqlQD/dJMcaccGcUi/6nQPxgMeDw3yTNFuNckKK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDqlQD%2FdJMcaccGcUi%2F6nQPxgMeDw3yTNFuNckKK1%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;740&quot; height=&quot;127&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 설계할 때, 멤버 변수와 멤버 함수는 생각하지 못했지만 클래스만 명시 해두고 싶을때, pass라는 키워드를 사용해 클래스 내부에 아무것도 넣지 않은 상태로 클래스를 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O0miZ/dJMcab5VqGD/bqM6RlDuKAuVp3xieLn9M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O0miZ/dJMcab5VqGD/bqM6RlDuKAuVp3xieLn9M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O0miZ/dJMcab5VqGD/bqM6RlDuKAuVp3xieLn9M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO0miZ%2FdJMcab5VqGD%2FbqM6RlDuKAuVp3xieLn9M0%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;720&quot; height=&quot;110&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 정의된 클래스를 이용해 인스턴스를 생성하려면 다음과 같이 클래스 이름 뒤에 ()를 넣으면 된다. 어떻게 보면 인스턴스를 생성하는 과정은 함수를 호출하는 것과 비슷다. 다음 코드가 실행되면 그림과 같이 'BusinessCard'라는 클래스의 인스턴스가 메모리의 0x0302ABB0 위치에 생성되고 card1이라는 변수가 이를 바인딩하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWlXQA/dJMcaf8ga9R/jW7C33N4jMUHJ4sTK2JySK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWlXQA/dJMcaf8ga9R/jW7C33N4jMUHJ4sTK2JySK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWlXQA/dJMcaf8ga9R/jW7C33N4jMUHJ4sTK2JySK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWlXQA%2FdJMcaf8ga9R%2FjW7C33N4jMUHJ4sTK2JySK%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;701&quot; height=&quot;192&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 클래스에서 내부함수의 첫번째로 self라는 변수사용한다. self는 해당 클래스로 생성된 인스턴스 자기자신을 의미하고 이를 인스턴스 변수라고 부른다. 실제로 함수를 호출할때에는 따로 인자를 전달 할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD0VaO/dJMb99NJMAi/uktagdQCsOCRrP16zZ3db0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD0VaO/dJMb99NJMAi/uktagdQCsOCRrP16zZ3db0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD0VaO/dJMb99NJMAi/uktagdQCsOCRrP16zZ3db0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD0VaO%2FdJMb99NJMAi%2FuktagdQCsOCRrP16zZ3db0%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;727&quot; height=&quot;257&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 내부함수 set_info로 내부 변수 name, emain, addr을 정의하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 따로 변수의 자료형을 선언하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서의 클래스 생성자는 __init__이라는이름을 갖는다. __init__이라는 함수를통해 생성과 동시에 내부변수를 초기화 할 수 있다. (소멸자는 __del__(self))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cb1wC/dJMcahrAnzC/aZ0MFnaBMiVttWtT4zsaEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cb1wC/dJMcahrAnzC/aZ0MFnaBMiVttWtT4zsaEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cb1wC/dJMcahrAnzC/aZ0MFnaBMiVttWtT4zsaEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCb1wC%2FdJMcahrAnzC%2FaZ0MFnaBMiVttWtT4zsaEK%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;677&quot; height=&quot;104&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 상속은 다음과 같이 인자에 부모클래스의 이름을 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQlfoP/dJMcaiKGJQz/FAKGY0nNfptgbUMClinW51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQlfoP/dJMcaiKGJQz/FAKGY0nNfptgbUMClinW51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQlfoP/dJMcaiKGJQz/FAKGY0nNfptgbUMClinW51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQlfoP%2FdJMcaiKGJQz%2FFAKGY0nNfptgbUMClinW51%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;916&quot; height=&quot;192&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOfse3/dJMcaaMIHMd/3HU1lJG7zJjHKCKMqh3me0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOfse3/dJMcaaMIHMd/3HU1lJG7zJjHKCKMqh3me0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOfse3/dJMcaaMIHMd/3HU1lJG7zJjHKCKMqh3me0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOfse3%2FdJMcaaMIHMd%2F3HU1lJG7zJjHKCKMqh3me0%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;781&quot; height=&quot;137&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;&lt;b&gt;실습&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&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;생성자와 자신의 멤버변수 데이터를 출력하는 print_info라는 멤버함수를 가진 Contact라는 연락처 클래스를 만들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Contact_list에서 연락처들을 연락처 입력, 출력, 삭제, 저장하며 관리하는 프로그램을 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1783520364456&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
&quot;&quot;&quot;
Created on Mon Jul  6 09:19:01 2026

@author: ihhan


선택메뉴
1. 연락처 입력
2. 연락처 출력
3. 연락처 삭제(이름기준)
4. 종료: txt파일로 저장

&quot;&quot;&quot;
class Contact:
    def __init__(self,name,phone_number, e_mail, addr):
        self.name=name
        self.phone_number=phone_number
        self.e_mail=e_mail
        self.addr=addr
        
    def print_info(self):
        print('=========================')
        print('이름:',self.name)
        print('전화:',self.phone_number)
        print('메일:',self.e_mail)
        print('주소:',self.addr)
        print('=========================')



#--1. 연락처 입력 ----------------------------------
def set_contact():
    name=input('이름 입력:')
    phone_number=input('전화번호 입력:')
    e_mail=input('메일 입력:')
    addr=input('주소 입력:')
    return Contact(name,phone_number,e_mail,addr)
    
    
#--2. 연락처 출력 ----------------------------------
# contact 객체가 가지고 있는 값(변수)을 출력 :2.
def print_contact(contact_list):
    for contact in contact_list:
        contact.print_info();
        
        
#--3. 연락처 삭제 ----------------------------------
# 이름을 통해 삭제 :3.

def delete_contact(contact_list,name):
    for i, contact in enumerate(contact_list):
        if contact.name ==name:
            del contact_list[i]
            
            
#--4. 연락처 저장 ----------------------------------
# 종료 선택시, 파일로 저장
def store_contact(contact_list):
    f=open('db.txt','wt')
    for contact in contact_list:
        f.write(contact.name+'\n')
        f.write(contact.phone_number+'\n')
        f.write(contact.e_mail+'\n')
        f.write(contact.addr+'\n')
    f.close()
#--사용자 선택 메뉴--------------------------------
def print_menu():
    print('1. 연락처 입력')
    print('2. 연락처 출력')
    print('3. 연락처 삭제')
    print('4. 연락처 저장')
    menu=input('메뉴 선택: ')
    return int(menu)

def run():
    
    contact_list = []
    while 1:
        menu=print_menu()
        if menu==1:
            contact = set_contact()
            contact_list.append(contact)
        elif menu==2:
            print_contact(contact_list)
        elif menu==3:
            name = input('삭제할 이름 입력: ')
            delete_contact((contact_list), name)
        elif menu==4:
            store_contact(contact_list)
            break
        else:
            print('잘못된 명령입니다.')
        
if __name__=='__main__':# 파일 자체 실행 여부 확인
     run()&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.&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;member.csv 파일을 읽어와서 이름, 등급, 최근방문일 데이터를 갖고 있는 Member 인스턴스에 저장해 member_list를 만든다.\&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783520571546&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Member:
    def __init__(self,name,rate,DateofLastVisit,NumberofVisit):#이름,멤버 등급,최종방문일,방문수
        self.name=name
        self.rate=rate
        self.DateofLastVisit=DateofLastVisit
        self.NumberofVisit=NumberofVisit
    def print_info(self):
        print('=========================')
        print('이름:',self.name)
        print('등급:',self.rate)
        print('최근방문일:',self.DateofLastVisit)
        print('=========================')
        
with open('member.csv', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    member_list = []
    
    
    for line in lines[1:]:              # [1:] &amp;rarr; 첫 줄(헤더)은 건너뜀
        line = line.strip()               # 줄바꿈 문자 제거
        if not line:                    # 빈 줄이면 건너뜀
            continue
        values = line.split(',')        # 쉼표 기준으로 쪼개서 리스트로 만듦
        m = Member(values[0], values[1], values[3], values[4])
        if int(m.NumberofVisit) &amp;gt;= 50:
            member_list.append(m)

# 결과 출력
    for m in member_list:
        m.print_info()&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;3.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqkpGQ/dJMcaijGczz/rXH1K34LMcXm8tQzLCerq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqkpGQ/dJMcaijGczz/rXH1K34LMcXm8tQzLCerq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqkpGQ/dJMcaijGczz/rXH1K34LMcXm8tQzLCerq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqkpGQ%2FdJMcaijGczz%2FrXH1K34LMcXm8tQzLCerq1%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;643&quot; height=&quot;670&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11만개의 병원 예약 정보가 담긴 엑셀 파일을 읽어와서, 진료일에 노쇼하는 환자들의 특징을 분석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 실습 1번처럼 사용자가 직접 성별, 예약 대기기간, 연령대 별로 교차분석을 할 수 있는 프로그램을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783520754765&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# -*- coding: utf-8 -*-
&quot;&quot;&quot;
Created on Mon Jul  6 15:54:00 2026

@author: ihhan
&quot;&quot;&quot;
from datetime import datetime
class Patient:
    def __init__(self,PatientId,AppointmentId,Gender,ScheduledDay,AppointmentDay,Age,Neighbourhood,SMS_received,No_show):
        self.PatientId=PatientId
        self.AppointmentId=AppointmentId
        self.Gender=Gender
        self.ScheduledDay=ScheduledDay
        self.AppointmentDay=AppointmentDay
        self.Age=Age
        self.Neighbourhood=Neighbourhood
        self.SMS_received=SMS_received
        self.No_show=No_show
    def print_info(self):
        print('=========================')
        print('환자ID:',self.PatientId)
        print('예약ID:',self.AppointmentId)
        print('성별:',self.Gender)
        print('예약일:',self.ScheduledDay)
        print('진료예정일:',self.AppointmentDay)
        print('나이:',self.Age)
        print('지역:',self.Neighbourhood)
        print('SMS수신동의:',self.SMS_received)
        print('노쇼여부:',self.No_show)
        print('=========================')
#함수화 하려면 특정 (표본/전체) 두개의 데이터를 반환하는게 좋을듯

with open('medical.csv', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    patient_list = []
       
    for line in lines[1:]:              # [1:] &amp;rarr; 첫 줄(헤더)은 건너뜀
        line = line.strip()               # 줄바꿈 문자 제거
        if not line:                    # 빈 줄이면 건너뜀
            continue
        values = line.split(',')        # 쉼표 기준으로 쪼개서 리스트로 만듦
        p = Patient(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8])
        patient_list.append(p)
        
                
    #연령별 노쇼율
    age_total = {
        &quot;0~19세&quot;: 0,
        &quot;20~39세&quot;: 0,
        &quot;40~59세&quot;: 0,
        &quot;60세 이상&quot;: 0
        }   

    age_noshow = {
        &quot;0~19세&quot;: 0,
        &quot;20~39세&quot;: 0,
        &quot;40~59세&quot;: 0,
        &quot;60세 이상&quot;: 0
        }

    for p in patient_list:

        age = int(p.Age)

        # 음수 나이는 제외
        if age &amp;lt; 0:
            continue

        if age &amp;lt; 20:
            group = &quot;0~19세&quot;
        elif age &amp;lt; 40:
            group = &quot;20~39세&quot;
        elif age &amp;lt; 60:
            group = &quot;40~59세&quot;
        else:
            group = &quot;60세 이상&quot;

        age_total[group] += 1

        if p.No_show == &quot;Yes&quot;:
            age_noshow[group] += 1
            
    print(&quot;\n연령대별 노쇼율&quot;)

    for group in [&quot;0~19세&quot;, &quot;20~39세&quot;, &quot;40~59세&quot;, &quot;60세 이상&quot;]:
        rate = age_noshow[group] / age_total[group]
        print(f&quot;{group:10} : {age_noshow[group]}/{age_total[group]} ({rate:.2%})&quot;)
# # 결과 출력
#     for p in patient_list:
#         p.print_info()
#--사용자 선택 메뉴--------------------------------
def print_menu():
    print('1. 성별별 분석 ')
    print('2. 예약 대기기간별 분석')
    print('3. 연령대별 분석')
    print('4. 교차분석')
    print('5. 종료')
    menu=input('메뉴 선택: ')
    return int(menu)
def analyzeByGender(g):
    #성별별 노쇼율
    male_count = 0
    male_noshow = 0

    female_count = 0
    female_noshow = 0

    for p in patient_list:
        if p.Gender == 'M':
            male_count += 1
            if p.No_show == 'Yes':    # 데이터에 맞게 'YES' 또는 'Yes' 사용
                male_noshow += 1

        elif p.Gender == 'F':
            female_count += 1
            if p.No_show == 'Yes':
                female_noshow += 1
    
    if(g=='M'):
        return [male_noshow,male_count]
    else:
        return [female_noshow,female_count]
    print(&quot;전체 남자:&quot;, male_count)
    print(&quot;노쇼한 남자:&quot;, male_noshow)
    print(&quot;남성 노쇼 비율:&quot;, male_noshow / male_count)

    print()
    
    print(&quot;전체 여자:&quot;, female_count)
    print(&quot;노쇼한 여자:&quot;, female_noshow)
    print(&quot;여성 노쇼 비율:&quot;, female_noshow / female_count)
def analyzeByWaiting(a):
    #대기 일수별 노쇼율

    group_total = {
        &quot;0~6일&quot;: 0,
        &quot;7~13일&quot;: 0,
        &quot;14~20일&quot;: 0,
        &quot;21~27일&quot;: 0,
        &quot;28일 이상&quot;: 0
    }

    group_noshow = {
        &quot;0~6일&quot;: 0,
        &quot;7~13일&quot;: 0,
        &quot;14~20일&quot;: 0,
        &quot;21~27일&quot;: 0,
        &quot;28일 이상&quot;: 0
        }

    for p in patient_list:

        scheduled = datetime.strptime(p.ScheduledDay[:10], &quot;%Y-%m-%d&quot;)
        appointment = datetime.strptime(p.AppointmentDay[:10], &quot;%Y-%m-%d&quot;)

        waiting_days = (appointment - scheduled).days

        if waiting_days &amp;lt; 0:
            continue

        if waiting_days &amp;lt; 7:
            group = &quot;0~6일&quot;
        elif waiting_days &amp;lt; 14:
            group = &quot;7~13일&quot;
        elif waiting_days &amp;lt; 21:
            group = &quot;14~20일&quot;
        elif waiting_days &amp;lt; 28:
            group = &quot;21~27일&quot;
        else:
            group = &quot;28일 이상&quot;

        group_total[group] += 1

        if p.No_show == &quot;Yes&quot;:
            group_noshow[group] += 1
        
    if(a==1):
        return [group_noshow[&quot;0~6일&quot;],group_total[&quot;0~6일&quot;]]
    elif(a==2):
        return [group_noshow[&quot;7~13일&quot;],group_total[&quot;7~13일&quot;]]
    elif(a==3):
        return [group_noshow[&quot;14~20일&quot;],group_total[&quot;14~20일&quot;]]
    elif(a==4):
        return [group_noshow[&quot;21~27일&quot;],group_total[&quot;21~27일&quot;]]
    elif(a==5):
        return [group_noshow[&quot;28일 이상&quot;],group_total[&quot;28일 이상&quot;]]

def analyzeByAge(a):
    #연령별 노쇼율
    age_total = {
        &quot;0~19세&quot;: 0,
        &quot;20~39세&quot;: 0,
        &quot;40~59세&quot;: 0,
        &quot;60세 이상&quot;: 0
        }   

    age_noshow = {
        &quot;0~19세&quot;: 0,
        &quot;20~39세&quot;: 0,
        &quot;40~59세&quot;: 0,
        &quot;60세 이상&quot;: 0
        }

    for p in patient_list:

        age = int(p.Age)

        # 음수 나이는 제외
        if age &amp;lt; 0:
            continue

        if age &amp;lt; 20:
            group = &quot;0~19세&quot;
        elif age &amp;lt; 40:
            group = &quot;20~39세&quot;
        elif age &amp;lt; 60:
            group = &quot;40~59세&quot;
        else:
            group = &quot;60세 이상&quot;

        age_total[group] += 1

        if p.No_show == &quot;Yes&quot;:
            age_noshow[group] += 1


    if(a==1):
        return [age_noshow[&quot;0~19세&quot;],age_total[&quot;0~19세&quot;]]
    elif(a==2):
        return [age_noshow[&quot;20~39세&quot;],age_total[&quot;20~39세&quot;]]
    elif(a==3):
        return [age_noshow[&quot;40~59세&quot;],age_total[&quot;40~59세&quot;]]
    elif(a==4):
        return [age_noshow[&quot;60세 이상&quot;],age_total[&quot;60세 이상&quot;]]
    
def run():
    
    with open('medical.csv', 'r', encoding='utf-8') as f:
        lines = f.readlines()
        patient_list = []
           
        for line in lines[1:]:              # [1:] &amp;rarr; 첫 줄(헤더)은 건너뜀
            line = line.strip()               # 줄바꿈 문자 제거
            if not line:                    # 빈 줄이면 건너뜀
                continue
            values = line.split(',')        # 쉼표 기준으로 쪼개서 리스트로 만듦
            p = Patient(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8])
            patient_list.append(p)
    while 1:
        menu=print_menu()
        if menu==1:
           g=input('분석할 성별을 입력하세요.(M/F)')
           result=analyzeByGender(g)
           print(&quot;전체&quot;,g,':',result[1])
           print(&quot;노쇼한&quot;,g,':',result[0])
           print(&quot;노쇼한&quot;,g,'비율:',result[0]/result[1])
        elif menu==2:
            a=int(input('분석할 대기일수를 입력하세요.(1: 0~6 2:7~13 3:14~20 4:21~27 5:28+)'))
            result=analyzeByWaiting(a)
            print(&quot;전체&quot;,a,':',result[1])
            print(&quot;노쇼한&quot;,a,':',result[0])
            print(&quot;노쇼한&quot;,a,'비율:',result[0]/result[1])
        elif menu==3:
            a=int(input('분석할 연령대를 입력하세요.(1: 0~19 2:20~39 3:40~59 4:60+)'))
            result = analyzeByAge(a) 
            print(&quot;전체&quot;,a,':',result[1])
            print(&quot;노쇼한&quot;,a,':',result[0])
            print(&quot;노쇼한&quot;,a,'비율:',result[0]/result[1])
        elif menu==4:
            no_showCnt=0
            totalCnt=0
            cross=input('교차 분석 할 표본을 선택하세요.(1: 성별 2: 대기일수 3:연령대)')
            cross_list = cross.split()
            for c in cross_list:
                if c == '1':
                    g=input('분석할 성별을 입력하세요.(M/F)')
                    result=analyzeByGender(g)
                    no_showCnt+=result[0]
                    totalCnt+=result[1]
                elif c == '2':
                    print(&quot;대기일수 교차분석 실행&quot;)
                    a=int(input('분석할 대기일수를 입력하세요.(1: 0~6 2:7~13 3:14~20 4:21~27 5:28+)'))
                    result=analyzeByWaiting(a)
                    no_showCnt+=result[0]
                    totalCnt+=result[1]
                elif c == '3':
                    print(&quot;연령대 교차분석 실행&quot;)
                    a=int(input('분석할 연령대를 입력하세요.(1: 0~19 2:20~39 3:40~59 4:60+)'))
                    result = analyzeByAge(a) 
                    no_showCnt+=result[0]
                    totalCnt+=result[1]
            print(&quot;전체 표본&quot;,':',totalCnt)
            print(&quot;노쇼한 표본&quot;,':',no_showCnt)
            print(&quot;노쇼한비율:&quot;,no_showCnt/totalCnt)
                            
        elif menu==5:
            
            break
        else:
            print('잘못된 명령입니다.')
        
if(__name__=='__main__'):
     run()&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;교차분석을 하기 위해서 각 함수별로 노쇼한@@@, 전체@@@를 담은 리스트를 반환하도록 했는데, 코드를 짜고 보니 사용자가 메뉴를 선택할 때마다 11만개의 데이터를 계속 순회하는 비효율적인 구조가 돼서 아쉬웠다.&amp;nbsp;&lt;/p&gt;</description>
      <category>파이썬</category>
      <author>인뽕</author>
      <guid isPermaLink="true">https://bb0ng2.tistory.com/1</guid>
      <comments>https://bb0ng2.tistory.com/1#entry1comment</comments>
      <pubDate>Wed, 8 Jul 2026 23:28:32 +0900</pubDate>
    </item>
  </channel>
</rss>