Python 및 Tkinter로 간단한 직업 추천 엔진 구축
Source: Dev.to
스킬에 기반해 직업을 추천해주는 데스크톱 앱을 만들고 싶으셨나요? 이 튜토리얼에서는 Python, Tkinter, 그리고 ttkbootstrap을 사용해 Job Recommendation Engine을 구축합니다.
다룰 내용:
- 프로젝트 설정
- 직업 데이터 구조 정의
- 직업 가져오기 및 필터링
- 간단한 추천 엔진 구축
- Tkinter UI로 결과 표시
- 페이지네이션 및 검색 추가
시작해봅시다! 🚀
1. 설정 및 의존성 설치
먼저 Python 3이 설치되어 있는지 확인하십시오. 그런 다음 필요한 패키지를 설치합니다:
pip install ttkbootstrap pillow requests
패키지 설명
| 패키지 | 목적 |
|---|---|
ttkbootstrap | 현대적인 Tkinter UI 스타일링 |
Pillow | 이미지(회사 로고) 처리 및 표시 |
requests | URL에서 로고 가져오기 |
2. 작업 데이터 구조 정의
각 작업을 나타내기 위해 dataclass를 사용할 것입니다. 이렇게 하면 작업 정보를 쉽게 저장하고 관리할 수 있습니다.
from dataclasses import dataclass
from typing import List
@dataclass
class Job:
title: str
company: str
url: str
description: str
skills: List[str]
location: str
logo_url: str = ""
score: float = 0.0
✅ 이는 각 작업에 제목, 회사, 위치, 기술, 로고 및 추천을 위한 점수를 부여합니다.
3. 채용 정보 가져오기
이 튜토리얼에서는 정적 리스트의 채용 정보를 사용합니다. 나중에 API 호출로 교체할 수 있습니다.
from typing import List
def fetch_jobs(query: str) -> List[Job]:
jobs_data = [
Job(
"Python Developer",
"TechCorp",
"https://example.com/job1",
"Develop backend applications using Python.",
["Python", "Django"],
"Remote",
"https://via.placeholder.com/100x100.png?text=TechCorp",
),
Job(
"Frontend Engineer",
"Webify",
"https://example.com/job2",
"Build responsive web apps with React.",
["JavaScript", "React"],
"NY, USA",
"https://via.placeholder.com/100x100.png?text=Webify",
),
Job(
"Data Scientist",
"DataWorks",
"https://example.com/job3",
"Analyze large datasets and build ML models.",
["Python", "ML", "Pandas"],
"SF, USA",
"https://via.placeholder.com/100x100.png?text=DataWorks",
),
]
# Filter jobs by query keyword
query_tokens = set(query.lower().split())
matched_jobs = []
for job in jobs_data:
text_to_match = " ".join(
[
job.title.lower(),
job.description.lower(),
" ".join(job.skills).lower(),
]
)
if any(token in text_to_match for token in query_tokens):
matched_jobs.append(job)
return matched_jobs
💡 Tip: 이 간단한 검색은 채용 제목, 설명 또는 기술에 포함된 키워드를 매칭합니다.
4. 간단한 추천 엔진 만들기
각 작업에 키워드 매치를 기반으로 점수를 부여하여 결과를 순위 매깁니다.
def recommend_jobs(query: str, candidates: List[Job], top_n: int = 5):
query_tokens = set(query.lower().split())
recommendations = []
for job in candidates:
text_to_match = " ".join(
[
job.title.lower(),
job.description.lower(),
" ".join(job.skills).lower(),
]
)
score = sum(1 for token in query_tokens if token in text_to_match)
recommendations.append((job, score))
recommendations.sort(key=lambda x: x[1], reverse=True)
return recommendations[:top_n]
✅ 점수가 높을수록 매치가 더 좋습니다. 이것은 간단한 콘텐츠 기반 추천입니다.
5. 회사 로고 로드
앱에서 이미지를 가져오고 표시하기 위해 Pillow를 사용할 것입니다.
import io
import requests
from PIL import Image, ImageTk
logo_cache = {}
def load_image(url: str, size: tuple = (80, 80)):
"""Download an image, resize it, and cache the PhotoImage."""
if not url:
return None
if url in logo_cache:
return logo_cache[url]
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
img = Image.open(io.BytesIO(resp.content))
img = img.resize(size, Image.ANTIALIAS)
photo = ImageTk.PhotoImage(img)
logo_cache[url] = photo
return photo
except Exception:
return None
💡 팁: 캐싱을 하면 동일한 이미지를 여러 번 다운로드하는 것을 방지할 수 있습니다.
6. Tkinter UI 구축
이제 ttkbootstrap을 사용하여 사용자 인터페이스를 구축해 보겠습니다.
import tkinter as tk
import ttkbootstrap as tb
from ttkbootstrap.widgets.scrolled import ScrolledText
# ----------------------------------------------------------------------
# Main window
# ----------------------------------------------------------------------
app = tb.Window(
title="Job Recommendation Engine",
themename="flatly",
size=(980, 720),
)
# ----------------------------------------------------------------------
# Top Section – Search Bar
# ----------------------------------------------------------------------
top = tb.Frame(app, padding=15)
top.pack(fill=tk.X)
tb.Label(
top,
text="Job Recommendation Engine",
font=("Segoe UI", 16, "bold"),
).pack(anchor=tk.W)
query_entry = tb.Entry(top, font=("Segoe UI", 12))
query_entry.pack(fill=tk.X, pady=8)
tb.Button(
top,
text="Search",
bootstyle="primary",
command=lambda: print("Search clicked!"),
).pack(anchor=tk.E)
# ----------------------------------------------------------------------
# Results Area
# ----------------------------------------------------------------------
result_frame = tb.Frame(app, padding=(15, 5))
result_frame.pack(fill=tk.BOTH, expand=True)
result_box = ScrolledText(result_frame, autohide=True)
result_box.pack(fill=tk.BOTH, expand=True)
text = result_box.text
text.configure(state="disabled", wrap="word")
# This is where job results will be displayed.
# ----------------------------------------------------------------------
# Pagination Controls
# ----------------------------------------------------------------------
nav = tb.Frame(app, padding=10)
nav.pack(fill=tk.X)
prev_btn = tb.Button(nav, text="← Prev", bootstyle="secondary")
prev_btn.pack(side=tk.LEFT)
page_label = tb.Label(nav, text="Page 1", font=("Segoe UI", 10))
page_label.pack(side=tk.LEFT, padx=10)
next_btn = tb.Button(nav, text="Next →", bootstyle="secondary")
next_btn.pack(side=tk.LEFT)
# ----------------------------------------------------------------------
# (Optional) Hook up pagination and search logic here
# ----------------------------------------------------------------------
💡 이제 Search, Prev, Next 버튼을 앞서 정의한 함수에 연결하여 결과 페이지를 탐색할 수 있습니다.
7. 모든 것을 하나로 묶기
아래는 모든 요소를 결합한 최소 예제입니다. 자세한 직무 보기, 즐겨찾기 저장 등 더 많은 기능을 추가해도 좋습니다.
def display_results(recommendations):
"""Populate the ScrolledText widget with job cards."""
text.configure(state="normal")
text.delete("1.0", tk.END)
for job, score in recommendations:
# Load logo (cached)
logo = load_image(job.logo_url)
# Build a simple text block for each job
block = f"""\
{job.title} ({job.location})
Company: {job.company}
Score: {score}
Skills: {', '.join(job.skills)}
Link: {job.url}
"""
text.insert(tk.END, block + "\n" + "-" * 40 + "\n\n")
text.configure(state="disabled")
def on_search():
query = query_entry.get()
candidates = fetch_jobs(query) # In a real app, fetch all jobs first
recommendations = recommend_jobs(query, candidates, top_n=10)
display_results(recommendations)
# Bind the search button
search_btn = tb.Button(
top,
text="Search",
bootstyle="primary",
command=on_search,
)
search_btn.pack(anchor=tk.E, pady=5)
# Start the Tkinter main loop
if __name__ == "__main__":
app.mainloop()
이것으로 끝입니다! 이제 Python, Tkinter, 그리고 ttkbootstrap으로 만든 실용적인 데스크톱 구직 추천 앱이 완성되었습니다. 🎉
더 정교한 추천 알고리즘을 실험하거나 실제 구직 API를 연동하고, UI를 다듬는 등 자유롭게 확장해 보세요. 즐거운 코딩 되세요!
검색 입력을 작업 검색, 추천 및 표시와 연결하기
threading을 사용하여 검색을 수행하는 동안 UI가 응답하도록 유지합니다.
import threading
def perform_search():
"""Called when the user presses the Search button."""
query = query_entry.get().strip()
if not query:
return
# Run the heavy work in a background thread
threading.Thread(target=search_thread, args=(query,), daemon=True).start()
def search_thread(query):
"""Background worker that fetches jobs, ranks them, and prints the results."""
candidates = fetch_jobs(query)
recommendations = recommend_jobs(query, candidates)
for job, score in recommendations:
print(f"{job.title} @ {job.company} | Score: {score}")
전체 화면 제어
- 전체 화면 모드 진입
- 전체 화면 모드 종료
직접 사용해 보기
-
스크립트를 실행합니다
python job_recommender.py -
검색 상자에 키워드(예:
Python또는React)를 입력합니다. -
작업 제목을 더블 클릭하여 링크를 엽니다(해당 기능을 구현한 경우).
-
Prev / Next 버튼으로 결과를 탐색합니다.
전체 소스 코드
전체 프로젝트는 GitHub에서 확인할 수 있습니다:
https://github.com/rogers-cyber/JOBREC
결론
축하합니다! 🎉 이제 Job Recommendation Engine이 정상적으로 작동합니다. 주요 기능:
- Python 데이터 클래스
- 간단한 콘텐츠 기반 추천
- 검색, 결과 및 페이지네이션이 포함된 Tkinter UI
다음 단계
- 정적 직업 목록을 실제 API 호출로 교체합니다.
- 더 똑똑한 검색을 위해 퍼지 매칭을 추가합니다.
- 이미지, 아이콘 및 필터를 포함하도록 UI를 개선합니다.
