Tkinter 화면을 Vue 화면으로 변환
Vue의 데이터 중심 웹 화면으로 바꾸는 방법을 배웁니다.
지금까지 Python에서는 Tkinter를 이용하여 데스크톱 프로그램의 화면을 만들었습니다.
Tkinter 화면 → Python 함수 → SQLite
이 프로그램을 웹 프로그램으로 변경하면 다음과 같은 구조가 됩니다.
Vue → REST API → FastAPI → SQLite
Python 프로그램 전체를 한 번에 바꾸는 것이 아니라, 먼저 Tkinter 화면을 Vue 화면으로 변경하는 방법부터 이해합니다.
Tkinter와 Vue는 사용하는 문법은 다르지만 화면을 만드는 기본 개념에는 비슷한 부분이 많습니다.
| Tkinter | Vue / Web |
|---|---|
| Tk() | 웹 페이지 / App |
| Frame | Component / View |
| Label | <label>, <p>, <span> |
| Entry | <input> |
| Text | <textarea> |
| Button | <button> |
| Combobox | <select> |
| Checkbutton | <input type="checkbox"> |
| Radiobutton | <input type="radio"> |
| Treeview | <table> + v-for |
| command | @click |
| StringVar | ref() 또는 reactive() |
| Frame 전환 | Vue Router |
| messagebox | alert(), confirm() |
새로운 개념을 처음부터 모두 배우는 것이 아니라 기존 Tkinter의 화면 개념을 웹 방식으로 바꾸어 생각하면 됩니다.
import tkinter as tk
window = tk.Tk()
window.title("사원관리")
label = tk.Label(
window,
text="사원관리 프로그램"
)
label.pack()
window.mainloop()
<template>
<div>
<h1>
사원관리 프로그램
</h1>
</div>
</template>
Vue에서는 Tkinter 위젯 대신 웹의 HTML 태그를 이용하여 화면을 만듭니다.
label = tk.Label(
window,
text="이름"
)
label.pack()
<template>
<label>
이름
</label>
</template>
상황에 따라 다음과 같이 사용할 수도 있습니다.
<p> 이름 </p>
Tkinter에서 문자열을 입력받을 때는 Entry를 사용합니다.
entry_name = tk.Entry(window) entry_name.pack()
Vue에서는 HTML의 input을 사용합니다.
<input>
실제 프로그램에서는 입력값을 변수와 연결해야 합니다.
name = tk.StringVar()
entry_name = tk.Entry(
window,
textvariable=name
)
entry_name.pack()
<script setup>
import { ref } from 'vue'
const name = ref('')
</script>
<template>
<input v-model="name">
</template>
Vue : ref ↔ input + v-model
StringVar → ref()
textvariable → v-model
name = entry_name.get() print(name)
print(name.get())
console.log(name.value)
Vue에서는 화면의 input 요소를 직접 찾아 값을 가져오는 방식보다 v-model로 연결된 데이터를 사용하는 방식이 기본입니다.
def save():
print("저장")
button = tk.Button(
window,
text="저장",
command=save
)
button.pack()
<script setup>
function save() {
console.log('저장')
}
</script>
<template>
<button @click="save">
저장
</button>
</template>
문법은 다르지만 버튼을 클릭했을 때 함수를 실행한다는 원리는 같습니다.
import tkinter as tk
from tkinter import messagebox
window = tk.Tk()
window.title("사원등록")
tk.Label(
window,
text="이름"
).pack()
entry_name = tk.Entry(window)
entry_name.pack()
def save():
name = entry_name.get()
messagebox.showinfo(
"확인",
name
)
tk.Button(
window,
text="등록",
command=save
).pack()
window.mainloop()
<script setup>
import { ref } from 'vue'
const name = ref('')
function save() {
alert(name.value)
}
</script>
<template>
<div>
<label>
이름
</label>
<input v-model="name">
<button @click="save">
등록
</button>
</div>
</template>
| Tkinter | Vue |
|---|---|
| Entry | input |
| StringVar | ref |
| textvariable | v-model |
| Button | button |
| command | @click |
| messagebox.showinfo | alert |
사원정보처럼 서로 관련된 입력값이 여러 개라면 Vue의 reactive() 객체로 묶어 관리할 수 있습니다.
import tkinter as tk
window = tk.Tk()
tk.Label(
window,
text="이름"
).pack()
entry_name = tk.Entry(window)
entry_name.pack()
tk.Label(
window,
text="전화번호"
).pack()
entry_phone = tk.Entry(window)
entry_phone.pack()
tk.Label(
window,
text="부서"
).pack()
entry_department = tk.Entry(window)
entry_department.pack()
window.mainloop()
<script setup>
import { reactive } from 'vue'
const employee = reactive({
name: '',
phone: '',
department: ''
})
</script>
<template>
<div>
<p>
이름 :
<input
v-model="employee.name"
>
</p>
<p>
전화번호 :
<input
v-model="employee.phone"
>
</p>
<p>
부서 :
<input
v-model="employee.department"
>
</p>
</div>
</template>
from tkinter import ttk
department = tk.StringVar()
combo = ttk.Combobox(
window,
textvariable=department
)
combo["values"] = (
"개발팀",
"영업팀",
"관리팀"
)
combo.pack()
<select
v-model="employee.department"
>
<option value="개발팀">
개발팀
</option>
<option value="영업팀">
영업팀
</option>
<option value="관리팀">
관리팀
</option>
</select>
text_memo = tk.Text(
window,
width=40,
height=10
)
text_memo.pack()
<textarea v-model="employee.memo" > </textarea>
const employee = reactive({
name: '',
phone: '',
department: '',
memo: ''
})
agree = tk.BooleanVar()
check = tk.Checkbutton(
window,
text="동의합니다.",
variable=agree
)
check.pack()
<input type="checkbox" v-model="agree" > 동의합니다.
const agree = ref(false)
Checkbutton → checkbox
gender = tk.StringVar()
tk.Radiobutton(
window,
text="남",
variable=gender,
value="남"
).pack()
tk.Radiobutton(
window,
text="여",
variable=gender,
value="여"
).pack()
<input type="radio" value="남" v-model="gender" > 남 <input type="radio" value="여" v-model="gender" > 여
const gender = ref('')
사원관리 프로그램에서 가장 많이 사용하는 화면 중 하나는 여러 데이터를 표시하는 목록 화면입니다.
from tkinter import ttk
tree = ttk.Treeview(
window,
columns=(
"id",
"name",
"department"
),
show="headings"
)
tree.heading(
"id",
text="번호"
)
tree.heading(
"name",
text="이름"
)
tree.heading(
"department",
text="부서"
)
tree.pack()
Treeview에 데이터를 추가할 때는 다음과 같이 작성합니다.
tree.insert(
"",
"end",
values=(
1,
"홍길동",
"개발팀"
)
)
<script setup>
import { ref } from 'vue'
const employees = ref([
{
id: 1,
name: '홍길동',
department: '개발팀'
},
{
id: 2,
name: '김철수',
department: '영업팀'
}
])
</script>
<template>
<table border="1">
<thead>
<tr>
<th>번호</th>
<th>이름</th>
<th>부서</th>
</tr>
</thead>
<tbody>
<tr
v-for="emp in employees"
:key="emp.id"
>
<td>
{{ emp.id }}
</td>
<td>
{{ emp.name }}
</td>
<td>
{{ emp.department }}
</td>
</tr>
</tbody>
</table>
</template>
Vue : employees 배열 → v-for → table
tree.insert(
"",
"end",
values=(
3,
"이영희",
"관리팀"
)
)
Tkinter에서는 화면의 Treeview 위젯에 직접 데이터를 추가합니다.
employees.value.push({
id: 3,
name: '이영희',
department: '관리팀'
})
Vue에서는 배열의 데이터를 변경합니다. 그러면 v-for로 연결된 화면이 자동으로 갱신됩니다.
→ 화면 변경
→ 반응형 데이터 변경
→ 화면 자동 변경
Vue에서는 먼저 화면을 바꾸려고 하지 않습니다. 데이터를 변경하면 Vue가 화면을 자동으로 갱신한다고 생각해야 합니다.
selected = tree.selection()
tree.delete(
selected[0]
)
function removeEmployee(id) {
employees.value =
employees.value.filter(
emp => emp.id !== id
)
}
<button
@click="
removeEmployee(emp.id)
"
>
삭제
</button>
Vue : 데이터 삭제 → 화면 자동 변경
Tkinter 프로그램이 커지면 Frame을 사용하여 화면을 여러 영역으로 나눌 수 있습니다.
Vue에서는 이와 비슷하게 Component를 이용합니다.
<script setup> import EmployeeForm from './components/EmployeeForm.vue' import EmployeeList from './components/EmployeeList.vue' </script> <template> <EmployeeForm /> <EmployeeList /> </template>
Tkinter에서는 여러 Frame을 만들고 필요한 Frame을 표시하는 방식으로 화면을 전환할 수 있습니다.
Vue에서는 각 화면을 View로 만들고 Vue Router로 전환합니다.
| URL | 화면 |
|---|---|
| / | 홈 |
| /employees | 사원목록 |
| /employees/new | 사원등록 |
from tkinter import messagebox
messagebox.showinfo(
"확인",
"저장되었습니다."
)
alert( '저장되었습니다.' )
result = messagebox.askyesno(
"확인",
"삭제하시겠습니까?"
)
const result = confirm( '삭제하시겠습니까?' )
교육 단계에서는 alert()와 confirm()을 사용하고, 실제 프로젝트에서는 별도의 알림창 또는 모달 컴포넌트로 변경할 수 있습니다.
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
window = tk.Tk()
window.title(
"사원관리 프로그램"
)
tk.Label(
window,
text="이름"
).grid(
row=0,
column=0
)
entry_name = tk.Entry(window)
entry_name.grid(
row=0,
column=1
)
tk.Label(
window,
text="전화번호"
).grid(
row=1,
column=0
)
entry_phone = tk.Entry(window)
entry_phone.grid(
row=1,
column=1
)
tk.Label(
window,
text="부서"
).grid(
row=2,
column=0
)
combo_department = ttk.Combobox(
window,
values=(
"개발팀",
"영업팀",
"관리팀"
)
)
combo_department.grid(
row=2,
column=1
)
def save():
name = entry_name.get()
phone = entry_phone.get()
department = (
combo_department.get()
)
print(
name,
phone,
department
)
messagebox.showinfo(
"확인",
"등록되었습니다."
)
tk.Button(
window,
text="등록",
command=save
).grid(
row=3,
column=0,
columnspan=2
)
window.mainloop()
이제 이 Tkinter 화면과 동일한 기능을 Vue 방식으로 다시 구성해 보겠습니다.
<script setup>
import { reactive } from 'vue'
const employee = reactive({
name: '',
phone: '',
department: '개발팀'
})
function save() {
console.log(
employee.name,
employee.phone,
employee.department
)
alert(
'등록되었습니다.'
)
}
</script>
<template>
<div>
<h2>
사원등록
</h2>
<p>
이름 :
<input
v-model="employee.name"
>
</p>
<p>
전화번호 :
<input
v-model="employee.phone"
>
</p>
<p>
부서 :
<select
v-model="employee.department"
>
<option value="개발팀">
개발팀
</option>
<option value="영업팀">
영업팀
</option>
<option value="관리팀">
관리팀
</option>
</select>
</p>
<button @click="save">
등록
</button>
</div>
</template>
Tkinter와 Vue 모두 이름, 전화번호, 부서를 입력하고 등록 버튼을 누르는 기능을 구현합니다. 차이는 화면과 데이터를 관리하는 방법입니다.
entry_name = tk.Entry(window)
<input v-model="employee.name">
name = entry_name.get()
employee.name
command=save
@click="save"
ttk.Combobox(...)
<select v-model="employee.department">
Tkinter에서는 다음과 같은 배치 관리자를 사용했습니다.
pack() grid() place()
button.place(
x=100,
y=200
)
웹에서는 일반적으로 CSS를 이용해 화면을 배치합니다.
<template>
<div class="form">
<input>
<button>
등록
</button>
</div>
</template>
<style scoped>
.form {
display: flex;
gap: 10px;
}
</style>
↓
Web : CSS / Flexbox / Grid
Vue는 화면의 데이터와 동작을 관리하고, 실제 배치는 HTML과 CSS가 담당합니다.
Tkinter 프로그램에서는 위젯을 직접 다루는 코드가 많이 사용됩니다.
entry_name.get()
entry_name.delete(
0,
tk.END
)
tree.insert(...)
tree.delete(...)
Vue에서는 화면 자체보다 데이터 중심으로 생각합니다.
employee.name = ''
employees.value.push(
newEmployee
)
Tkinter에서는 위젯을 직접 조작하는 경우가 많지만, Vue에서는 데이터를 변경하면 화면이 자동으로 변경됩니다.
이 사고방식의 차이를 이해하는 것이 Tkinter 프로그램을 Vue로 변경할 때 가장 중요합니다.
기존 Tkinter 프로그램을 Vue로 변경할 때는 다음 순서로 진행하면 이해하기 쉽습니다.
현재 화면에 어떤 입력창, 버튼, 목록이 있는지 먼저 확인합니다.
하나의 화면인지, 재사용할 부분인지 구분합니다.
Label을 label, p, span, h1 등의 HTML 태그로 변경합니다.
입력창과 Vue 데이터를 연결합니다.
버튼 클릭 이벤트를 Vue 방식으로 변경합니다.
목록을 배열 데이터와 반복 렌더링 방식으로 변경합니다.
화면을 Vue 컴포넌트 구조로 나눕니다.
URL을 이용해 목록, 등록, 상세 등의 화면을 전환합니다.
중요한 것은 Python 코드를 한 줄씩 JavaScript나 Vue 코드로 바꾸는 것이 아닙니다. 기존 화면이 제공하던 기능을 Vue 방식으로 다시 구성하는 것입니다.
| Tkinter | Vue / Web | 의미 |
|---|---|---|
| Tk() | App / 웹 페이지 | 프로그램 기본 화면 |
| Frame | Component / View | 화면 분리 |
| Label | label / p / span | 문자 표시 |
| Entry | input | 한 줄 입력 |
| Text | textarea | 여러 줄 입력 |
| Combobox | select | 선택 목록 |
| Checkbutton | checkbox | 체크 입력 |
| Radiobutton | radio | 하나 선택 |
| Treeview | table + v-for | 목록 출력 |
| StringVar | ref() | 반응형 단일 데이터 |
| 여러 변수 | reactive() | 관련 데이터 묶음 |
| textvariable | v-model | 화면과 데이터 연결 |
| command | @click | 버튼 이벤트 |
| messagebox.showinfo | alert() | 알림 |
| messagebox.askyesno | confirm() | 확인 |
| Frame 전환 | Vue Router | 화면 이동 |
| pack / grid / place | CSS / Flexbox / Grid | 화면 배치 |
- 기존 Python 프로그램의 Tkinter 화면은 Vue 화면으로 변경할 수 있습니다.
- Tkinter의 Label은 HTML의 label, p, span 등의 태그로 표현합니다.
- Tkinter의 Entry는 Vue에서 input으로 변경합니다.
- StringVar와 textvariable의 역할은 Vue의 ref()와 v-model로 연결해서 생각할 수 있습니다.
- 여러 개의 관련 입력값은 reactive() 객체로 묶어 관리할 수 있습니다.
- Tkinter의 command는 Vue에서 @click 이벤트로 변경합니다.
- Combobox는 select, Text는 textarea로 변경할 수 있습니다.
- Checkbutton은 checkbox, Radiobutton은 radio로 표현합니다.
- Treeview 목록은 Vue에서 table + 배열 + v-for 구조로 만들 수 있습니다.
- Tkinter에서는 Treeview에 데이터를 직접 추가하지만 Vue에서는 배열 데이터를 변경하면 화면이 자동으로 갱신됩니다.
- Tkinter의 Frame은 Vue의 Component 또는 View와 연결해서 이해할 수 있습니다.
- 여러 Frame을 바꾸어 표시하던 방식은 Vue에서 Vue Router를 이용한 화면 전환으로 변경할 수 있습니다.
- messagebox.showinfo는 교육 단계에서 alert()로 처리할 수 있습니다.
- messagebox.askyesno는 confirm()으로 처리할 수 있습니다.
- Tkinter의 pack, grid, place와 같은 배치는 웹에서는 CSS, Flexbox, Grid가 담당합니다.
- Tkinter는 위젯을 직접 조작하는 방식이 많지만, Vue는 데이터를 변경하여 화면을 자동으로 갱신하는 방식이 핵심입니다.
- Tkinter 코드를 한 줄씩 Vue 코드로 번역하는 것이 아니라 기존 화면의 기능을 Vue 방식으로 다시 구성해야 합니다.