데이터 분석

0721 아키텍처/데이터파이프라인 +GUI

인뽕 2026. 7. 21. 17:12

 

아키텍처 (Architecture)

 

IT 분야에서 아키텍처(Architecture)란
소프트웨어나 시스템을 구성하는 핵심 요소들과
그 요소들이 상호작용하는 방식
그리고 전체 시스템을 설계하고 발전시키는 기반 규칙을 의미

쉽게 말해 건물을 지을 때 뼈대와 배관, 전기 회선의 위치를 잡는 '건축 설계도'와 같다.
인테리어(UI/UX)를 꾸미기 전에 건물이 무너지지 않고 효율적으로 기능하도록 튼튼한 구조를 잡는 작업

아키텍처의 핵심 구성 요소

아키텍처는 보통 다음 세 가지를 정의.

컴포넌트(Components):
시스템을 이루는 조각들입니다.
프론트엔드(화면), 백엔드 서버, 데이터베이스, 외부 API 등이 여기에 해당합니다.

관계와 상호작용(Interactions):
컴포넌트들이 어떻게 데이터를 주고받는지 정의.(우리는 데이터 파이프라인)
(예: "사용자가 로그인하면, 서버는 데이터베이스에서 정보를 확인하고 화면에 결과를 반환한다.")

제약 조건과 규칙(Rules & Constraints):
보안 기준, 데이터 저장 방식, 트래픽이 몰렸을 때 서버를 늘리는 방식 등
시스템이 지켜야 할 원칙.

주요 아키텍처의 종류

관점에 따라 여러 가지로 나뉘지만, 대표적으로 두 가지.

시스템 아키텍처 (인프라 관점):
하드웨어, 네트워크, 클라우드 서버, 데이터베이스 등
물리적/가상적 자원들이 어떻게 배치되고 연결되는지?.

소프트웨어 아키텍처 (코드 관점):
개발자들이 작성하는 애플리케이션 코드를
어떤 패턴(예: MVC 패턴)과 구조로 분리하여 짤 것인지?.

가장 대표적인 아키텍처 패턴 비교

모놀리식(Monolithic)은 모든 시설이 한 층에 모여 있는 대형 마트이고,
마이크로서비스(MSA)는 개별 매장들이 골목에 모여 상권을 이룬 전통 시장!

1. 모놀리식(Monolithic) 아키텍처

소프트웨어의 모든 기능(사용자 관리, 결제, 게시판 등)과
데이터베이스 접근 로직이 단일 코드베이스로 작성되고,
하나의 통짜 애플리케이션으로 배포되는 전통적인 구조.

장점:
초기 개발 속도가 매우 빠르다.
코드가 한곳에 있어 디버깅이나 흐름을 파악하기 쉽다.
하나의 결과물로 빌드되므로 테스트와 배포 과정이 단순.

단점:
서비스가 커질수록 코드가 방대해져
작은 수정 하나를 위해 전체 시스템을 다시 빌드하고 배포해야 한다.
특정 기능(예:결제)에 트래픽이 몰려도 전체 서버의 사양을 높여야 하므로 비효율적.
한 부분의 치명적인 버그가 전체 시스템을 멈추게 할 수 있다.

2. 마이크로서비스 아키텍처 (MSA)

애플리케이션을 핵심 비즈니스 기능(예: 회원 서비스, 주문 서비스, 결제 서비스) 단위로 잘게 쪼개고,
각각 독립적으로 작동하는 서비스들의 조합으로 구축하는 방식.
서비스들은 주로 API(네트워크)를 통해 서로 통신.

장점:
부분적인 수정과 독립 배포가 가능.
다른 서비스에 영향을 주지 않고 하루에도 수십 번씩 업데이트할 수 있다.
트래픽이 몰리는 특정 서비스(예: 이벤트 기간의 '주문' 서비스)만
서버를 늘려 유연하게 대처할 수 있다.
하나의 서비스가 다운되어도 전체 시스템은 계속 작동(장애 격리).

단점:
네트워크 통신이 필수적이므로
전체적인 구조와 트랜잭션 관리가 매우 복잡해진다.
여러 서비스에 걸쳐 있는 오류를 추적(디버깅)하고 통합 테스트를 하기가 어렵다.
초기 아키텍처 설계와 인프라 구축 비용이 높다.

=========================================================

데이터 파이프라인(Data Pipeline)

데이터 파이프라인은
다양한 소스(출처)에서 발생한 원시 데이터(Raw Data)를 수집하여,
분석이나 활용이 가능한 형태로 가공한 뒤,
목적지(데이터 웨어하우스, 데이터 레이크 등)로 안전하게 이동시키는
'자동화된 데이터 흐름 체계'를 말한다.

데이터 웨어하우스 (Data Warehouse) :
개념: 분석을 위해 사전에 정의된 규칙(스키마)에 맞게 잘 정제되고 구조화된 데이터만 모아두는 중앙 저장소.
데이터 형태: 엑셀이나 관계형 데이터베이스(RDB)처럼 행(Row)과 열(Column)이 명확한 구조화된 데이터(Structured Data)만 저장

데이터 레이크 (Data Lake)
비유: 비, 강물, 지하수 등 다양한 형태의 물이 자연 그대로 흘러들어와 고이는 '거대한 자연 호수'
개념: 데이터의 용도나 형태를 미리 정하지 않고, 생성된 원시 데이터(Raw Data)를 가공하지 않은 상태 그대로 무한정 쏟아붓는 방대한 저장소

데이터 레이크하우스:
데이터 레이크의 유연하고 저렴한 스토리지 위에,
데이터 웨어하우스의 강력한 데이터 관리와 빠른 검색 성능(SQL 엔진)을 얹은 통합 플랫폼

쉽게 비유하자면 '정수장의 상수도 시스템'과 같다.
강물이나 호수(다양한 데이터 소스)에서 물을 끌어와,
파이프를 통해 정수장으로 보내 불순물을 걸러내고(데이터 가공 및 정제),
최종적으로 각 가정의 수도꼭지(데이터 분석가, AI 모델 등 목적지)로 깨끗한 물을 배달하는전체 인프라가 바로 데이터 파이프라인.

핵심 구성 요소 (ETL 과정)

데이터 파이프라인은 일반적으로 ETL이라는 세 가지 주요 단계를 거쳐 구축. 

(최근에는 클라우드의 발달로 순서가 바뀐 ELT 방식도 많이 쓰입니다.)



추출 (Extract):

  웹사이트 접속 로그, 결제 시스템의 트랜잭션, 소셜 미디어 API, 스마트폰 앱 등

 여러 곳에 흩어져 있는 원시 데이터를 수집하여 가져오는 단계.



변환 (Transform):

 가져온 듬성듬성한 데이터를 분석하기 좋게 쓸모 있게 만드는 과정.

 중복이나 오류 데이터를 제거하고(정제), 날짜 형식을 통일하며,

 민감한 개인정보를 마스킹(암호화)하는 등의 작업이 이루어진다.



적재 (Load):

 잘 가공된 데이터를 데이터 웨어하우스(Data Warehouse)나 클라우드 저장소 등 최종 목적지에 저장하여,

 분석가나 마케터가 언제든 꺼내 쓸 수 있게 만든다.



데이터 파이프라인이 왜 중요한가요?

수작업 및 오류 감소:

 과거에는 개발자나 데이터 담당자가 직접 엑셀이나 쿼리로 데이터를 모으고 가공했다면,

 파이프라인은 이를 24시간 자동으로 처리하여 업무 효율을 높이고 휴먼 에러를 방지.



신속한 의사결정 (Real-time):

 데이터가 실시간 또는 정해진 주기마다 빠르게 업데이트되므로,

 기업은 최신 데이터를 바탕으로 즉각적인 비즈니스 결정을 내릴 수 있다.



AI와 빅데이터의 핵심 기반:

 인공지능 모델을 학습시키거나 비즈니스 대시보드를 만들 때

 가장 중요한 것은 '깨끗하고 믿을 수 있는 데이터'.

 튼튼하고 안정적인 파이프라인 없이는 AI 도입이나 데이터 기반 의사결정이 불가능!!!.




GUI-tkinter 심화

 

앞 장에서 tkinter의 기본 위젯과 이벤트 처리 흐름을 익혔다면, 이번 장에서는 GUI 프로그램을 더 체 계적으로 구성하는 방법을 살펴봅니다. 간단한 프로그램은 함수 중심의 절차적 코드만으로도 충분히 만들 수 있습니다. 하지만 버튼 하나, 입력창 하나가 늘어날 때마다 전역 변수가 많아지고, 위젯과 함수가 서로 얽히면서 코드를 읽고 고치 기가 점점 어려워집니다. 기능이 많아질수록 "동작하는 코드"를 넘어 "정리된 코드"를 고민해야 하는 이유입니다. 먼저 절차적 코드가 어디에서 한계를 드러내는지 확인하 고, 이를 객체 지향 프로그래밍(OOP)으로 다시 구성하면 어떤 점이 나아지는지 비교해 봅니다. 이어서 Model(데이터와 비즈니스 로직), View(화면표시와 사용자 입력받기), Presenter (Model과 View를 연결하는 중재자) 로 프로그램의 코드를 역할에 따라 나누는 아키텍처를 가볍게 맛봅니다.

 

데이터 상태와 바인딩

 

GUI 프로그램을 작성하다 보면 한 번쯤 다음과 같은 고민을 하게 됩니다. "입력창에 적힌 값을 어떻게 가져오지?" "버튼을 누르면 화면의 글자를 어떻게 바꾸지?" 단순한 구조에서는 위젯을 직접 제어하여 대응할 수 있지만, UI가 복잡해질수록 데이터의 흐름과 상 태 변화를 수동으로 추적하는 것은 유지보수를 크게 저하시킵니다. 이 장에서는 이러한 복잡성을 해소할 핵심 개념으로 Tkinter 변수 클래스와 데이터 바인딩(Data Binding)을 다룹니다. 이를 적용하면 위젯을 직접 조작하는 방식이 아니라 데이터의 상태 변화만으 로 화면이 자동 동기화되는 효율적인 프로그램을 작성할 수 있습니다.

 

상태(State)란?

 

상태란 한마디로 '프로그램이 지금 이 순간 기억하고 있는 데이터' 를 말합니다. 거창해 보이지만, 우리가 화면에서 보는 거의 모든 것이 상태입니다. 체크박스에 체크가 되어 있는가, 아닌가 사용자가 입력창에 어떤 글자를 적었는가 지금 화면에 보이는 숫자가 몇인가 이런 것들이 모두 프로그램의 '상태'입니다. 프로그램을 만든다는 것은 결국 이 상태를 잘 보관하 고, 잘 바꾸고, 화면에 잘 보여 주는 일 이라고 할 수 있습니다.

 

데이터 바인딩이란?

 

문제는, 우리가 평소에 쓰던 일반 변수로는 상태를 화면과 연결하기가 번거롭다는 점입니다.

 

my_name = "홍길동" # 일반 변수

 

이렇게 만든 변수는 값이 바뀌어도 화면에 그 사실을 스스로 알려 주지 않습니다. 화면을 새 값으 로 바꾸고 싶다면, 우리가 코드로 매번 직접 라벨이나 텍스트를 갱신해 주어야 합니다. 비유하자면, 일반 변수는 "내 값이 바뀌었어!"라고 아무에게도 말하지 않는

조용한 변수 입니다. 데이터 바인딩은 이 불편함을 없애 줍니다. 데이터 바인딩이란 '데이터(변수)'와 '화면(위젯)'을 하 나로 묶어(Bind) 버리는 것 입니다. 한 번 묶어 두면 다음이 자동으로 일어납니다. 변수의 값을 바꾸면 → 화면이 알아서 바뀐다 화면에서 사용자가 값을 바꾸면 → 변수 안의 데이터도 알아서 바뀐다 즉, 우리는 화면이 아니라 변수 하나만 신경 쓰면 됩니다. 나머지는 tkinter가 대신 처리해 줍니다.

 

상태와 데이터 바인딩

 

tkinter 변수 클래스

 

데이터 바인딩을 하려면, 파이썬의 일반 변수 대신 tkinter 전용 변수 클래스를 써야 합니다. 이 변수들은 값이 바뀔 때마다 화면에 "값이 바뀌었어!"라고 신호를 보내 주는 특별한 변수입니다.

 

 

.set(), .get() 사용으로 값을 다룸

 

 

#라벨과 변수 묶기

# 기존의 변수 사용

import tkinter as tk

root = tk.Tk()
message = tk.StringVar()
message.set("초기값")


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

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

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

root.mainloop()

결과

값 변경 버튼을 눌러도, 라벨의 text=message.get()(일반변수)를 사용했기 때문에 처음에 저장한 "초기값"이라는 문자열이 변하지 않는다.

 

# textvariable 방식 (자동 갱신됨)

import tkinter as tk

root = tk.Tk()
message = tk.StringVar()
message.set("초기값")

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

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

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

root.mainloop()

결과

textvariable=message 인자를 사용했기 때문에, 값 변경을 누르고 message.set한 문자열이 실시간으로 출력된다.

 

#라디오 버튼
#여러 선택지 중 하나만 고르는 라디오버튼은 IntVar 로 관리합니다.
#각 라디오버튼에 value 값을 정해 주고, 같은 변수에 묶어 두면 어떤 것을 골랐는지
# 변수 하나로알 수 있습니다.
# 라디오 버튼 선택에 따른 라벨 실시간 출력

 

import tkinter as tk
def show_result():
    label.config(text="난이도 : " + level_var.get())

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

root.mainloop()

 

결과

 

text는 라디오 버튼 옆에 표시 될 글자.

variable은 k.StringVar(value="쉬움") 객체.

value는 해당 라디오 버튼이 선택되었을 때 level_var에 실제로 저장될 값.

command는 사용자가 라디오버튼 중 하나를 클릭할 때마다 실행될 함수를 지정.

anchor은 라디오버튼을 정렬할 방향.(서쪽)

 

 

#"OOP 기반 GUI 구조 설계" 의 중요성을 이해하기 위한 단계별
# To-do list 프로그램 만들기

 

import tkinter as tk
root = tk.Tk()
root.geometry("300x250")
listbox = tk.Listbox(root)
listbox.pack(fill="both", expand=True, padx=10, pady=(0, 7))
entry = tk.Entry(root)
entry.pack(fill="x", 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="추가", command=add_item)
button.pack(pady=(0, 10))

root.mainloop()

 

결과

 

# 삭제 기능 추가

import tkinter as tk
root = tk.Tk()
root.geometry("300x400")
listbox = tk.Listbox(root)
listbox.pack(fill="both", expand=True, padx=10, pady=(0, 7))

entry = tk.Entry(root)
entry.pack(fill="x", 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="추가", command=add_item)
add_button.pack(pady=(0, 5))

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

root.mainloop()

결과

리스트박스의 항목을 선택하고 삭제 버튼을 누르면 삭제 되는 기능 추가.

 

# 완료 처리 추가

 

import tkinter as tk

root = tk.Tk()
root.geometry("300x400")

total_count = 0
done_count = 0

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

entry = tk.Entry(root)
entry.pack(fill="x", padx=10, pady=10)

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

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

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], "[완료] " + 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="추가", command=add_item)
add_button.pack()

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

delete_button = tk.Button(root, text="삭제", command=delete_item)
delete_button.pack()

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

root.mainloop()

결과

완료한 일정을 표시하는 기능과 전체 삭제 기능 추가.

아따 길다 길어

 

'''
절차적 코드의 4가지 한계

#global 키워드의 남발

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

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

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

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

#상태와 동작의 분리

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

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

'''

 

 

#OOP 기반 GUI 구조 설계

 

'''
클래스 도입에 따른 구조적 장점

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

self란?
클래스 안에서 "자기 자신"을 가리키는 이름입니다. 
self.label 은 "이 객체가 가진
label "이라는 뜻으로, 객체 안 어디서든 같은 위젯을 가리킵니다.
'''

 

# Tk 직접 상속

 

import tkinter as tk
class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("300x80")
        self.label = tk.Label(self, text="안녕하세요")
        self.label.pack(pady=5)
        tk.Button(self, text="클릭", command=self.on_click).pack()
    def on_click(self):
        self.label.config(text="버튼을 눌렀습니다")

if __name__ == "__main__":
    Application().mainloop()

위젯을 self.label 처럼 속성으로 두었기 때문에,on_click 안에서 바로 접근할 수 있 습니다.
위젯을 일일이 전역 변수로 두거나 인자로 주고받을 필요가 없습니다.

 


#Frame 상속
#재사용 가능한 부품으로 캡슐화 실무에서는 Tk를 직접 상속하기보다 Frame 을 상속하는 방식을 더 권장합니다.
#Frame 은 여러 위젯을 담는 컨테이너이므로, 이를 상속한 클래스는 그 자체로 하나의 "부품(컴
#포넌트)"이 됩니다. 여러 개를 조합하거나 다른 창에 끼워 넣기 쉽습니다.

 

import tkinter as tk

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

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

if __name__ == "__main__":
    root = tk.Tk()
    CounterFrame(root).pack(padx=20, pady=20)
    root.mainloop()

 

결과

tk.Tk()를 우선 실행하여 root를 만들고, 위젯들을 프레임 단위로 관리한다.

 

#두번째 예제

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="left")
        tk.Button(self, text="인사하기", command=self.submit).pack(side="left")

    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="", font=("맑은 고딕", 14))
        self.label.pack()
    def show(self, name):
        self.label.config(text=f"안녕하세요, {name}님!")
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("300x120")
        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__ == "__main__":
    App().mainloop()

결과

 

#세번째 예제

#다중 페이지 전환
#페이지를 겹쳐 두고 바꿔 올리기
#로그인 화면에서 메인 화면으로 넘어가듯 여러 페이지를 전환해야 할 때, 각 페이지를 Frame 으로
#만들어 같은 위치에 겹쳐 둔 뒤 보여 줄 페이지만 맨 위로 끌어올리는 패턴을 자주 씁니다.

import tkinter as tk
class PageOne(tk.Frame):
    def __init__(self, master, controller):
        super().__init__(master)
        tk.Label(self, text="첫 번째 페이지", font=("맑은 고딕", 16)).pack(pady=20)
        tk.Button(self, text="다음으로",
                  command=lambda: controller.show_page("PageTwo")).pack()
class PageTwo(tk.Frame):
    def __init__(self, master, controller):
        super().__init__(master)
        tk.Label(self, text="두 번째 페이지", font=("맑은 고딕", 16)).pack(pady=20)
        tk.Button(self, text="이전으로",
                  command=lambda: controller.show_page("PageOne")).pack()
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("300x200")
        self.pages = {}
        for PageClass in (PageOne, PageTwo):
            page = PageClass(self, self)
            self.pages[PageClass.__name__] = page
            page.grid(row=0, column=0, sticky="nsew")
        self.show_page("PageOne")
    def show_page(self, name):
        self.pages[name].tkraise()
if __name__ == "__main__":
    App().mainloop()

실행시
다음으로 버튼 누를 시

 

처음에는 App 클래스 부분이 이해가 잘 안됐다.

생성자에서, 부모(tk.Tk)의 생성자를 호출하고,  pages라는 페이지들의 객체를 요소로 갖는 딕셔너리를 정의한다.

PageOne, PageTwo 클래스를 묶은 튜플을 순회하여 각 클래스의 생성자를 호출한다.

생성된 page를 pages의 요소로 넣는다. key는 클래스 이름(PageOne, PageTwo), value는 해당 페이지.

 

 

 

 

 

 

 

 

 

 

 

 

'데이터 분석' 카테고리의 다른 글

0722 MVP, 스레드  (1) 2026.07.22
0720  (0) 2026.07.20