๐ŸŽจ Python์œผ๋กœ Background Generator Tool ๋งŒ๋“ค๊ธฐ (๋‹จ๊ณ„๋ณ„)

๋ฐœํ–‰: (2026๋…„ 1์›” 19์ผ ์˜คํ›„ 06:13 GMT+9)
7 min read
์›๋ฌธ: Dev.to

Iโ€™m ready to translate the article for you, but Iโ€™ll need the full text youโ€™d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, Iโ€™ll keep the source line unchanged and translate the rest into Korean while preserving all formatting, markdown, and code blocks.

๋„๊ตฌ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

LibraryPurpose
Tkinter + ttkbootstrap๋‹คํฌ/๋ผ์ดํŠธ ํ…Œ๋งˆ๋ฅผ ์ง€์›ํ•˜๋Š” ํ˜„๋Œ€์ ์ธ GUI
Pillow (PIL)์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฐ ์กฐ์ž‘
NumPy๋น ๋ฅธ ํ”ฝ์…€โ€‘๋ ˆ๋ฒจ ๋…ธ์ด์ฆˆ ์ƒ์„ฑ
random & time๋ฌด์ž‘์œ„์„ฑ ๋ฐ ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ๋ช…

๐Ÿ‘‰ ์ตœ์ข… ํ”„๋กœ์ ํŠธ (GitHub):

๐Ÿ“Œ What Youโ€™ll Learn

  • How to generate gradients programmatically
  • How to add noise & texture to images
  • How to build a modern Tkinter GUI
  • How to resize images for live previews
  • How to export images with smart filenames

๐Ÿงฐ Stepโ€ฏ1 โ€“ ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

pip install ttkbootstrap pillow numpy

์™œ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๊ฐ€์š”?

  • ttkbootstrap โ€“ Tkinter์šฉ ํ˜„๋Œ€์ ์ธ ๋‹คํฌ/๋ผ์ดํŠธ ํ…Œ๋งˆ
  • Pillow โ€“ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฐ ์กฐ์ž‘
  • NumPy โ€“ ๋น ๋ฅธ ํ”ฝ์…€โ€‘๋ ˆ๋ฒจ ๋…ธ์ด์ฆˆ ์ƒ์„ฑ

๐Ÿ“ฆ Stepโ€ฏ2 โ€“ ํ•„์š”ํ•œ ๋ชจ๋“ˆ ๊ฐ€์ ธ์˜ค๊ธฐ

import ttkbootstrap as tb
from ttkbootstrap.constants import *
from tkinter import filedialog, messagebox
from tkinter import ttk
from PIL import Image, ImageDraw, ImageFilter, ImageTk
import numpy as np
import random
import time

๐Ÿ”น ์ด ์„ค์ •์€ GUI ์š”์†Œ, ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋„๊ตฌ๋ฅผ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽ› Stepโ€ฏ3 โ€“ Add Noise to an Image

def add_noise(img, intensity=10):
    arr = np.array(img)
    noise = np.random.randint(-intensity, intensity + 1, arr.shape, "int16")
    noisy = np.clip(arr + noise, 0, 255).astype("uint8")
    return Image.fromarray(noisy)

๐Ÿง  Whatโ€™s happening?

  1. Convert the image to a NumPy array
  2. Add random pixel values
  3. Clamp values between 0โ€ฏโ€“โ€ฏ255
  4. Convert back to a PIL image

๐ŸŒˆ ๋‹จ๊ณ„โ€ฏ4 โ€“ ์ˆ˜์ง ๊ทธ๋ผ๋””์–ธํŠธ ์ƒ์„ฑ

def generate_gradient(size):
    w, h = size
    top_color = (
        random.randint(50, 255),
        random.randint(50, 255),
        random.randint(50, 255)
    )
    bottom_color = (
        random.randint(50, 255),
        random.randint(50, 255),
        random.randint(50, 255)
    )

    img = Image.new("RGB", size)
    draw = ImageDraw.Draw(img)

    for y in range(h):
        r = int(top_color[0] + (bottom_color[0] - top_color[0]) * y / h)
        g = int(top_color[1] + (bottom_color[1] - top_color[1]) * y / h)
        b = int(top_color[2] + (bottom_color[2] - top_color[2]) * y / h)
        draw.line((0, y, w, y), fill=(r, g, b))

    return add_noise(img, intensity=5)

๐ŸŽจ ์„ค๋ช…: ๊ฐ ํ–‰์€ ์œ„์ชฝ โ†’ ์•„๋ž˜์ชฝ์œผ๋กœ ์ƒ‰์ƒ ๊ฐ’์„ ๋ณด๊ฐ„ํ•ฉ๋‹ˆ๋‹ค.

โœจ Stepโ€ฏ5 โ€“ ํ…์Šค์ฒ˜ ์˜ค๋ฒ„๋ ˆ์ด ๋งŒ๋“ค๊ธฐ

def generate_texture(size):
    img = Image.new("RGBA", size)
    draw = ImageDraw.Draw(img)

    for _ in range(size[0] * size[1] // 50):
        x = random.randint(0, size[0] - 1)
        y = random.randint(0, size[1] - 1)
        color = (
            random.randint(100, 255),
            random.randint(100, 255),
            random.randint(100, 255),
            random.randint(20, 50)
        )
        draw.point((x, y), fill=color)

    return img

๐Ÿ”น ์ด ์ฝ”๋“œ๋Š” ์ด๋ฏธ์ง€ ์ „์ฒด์— ๋ฐ˜ํˆฌ๋ช… ํ”ฝ์…€์„ ๋ฌด์ž‘์œ„๋กœ ํฉ๋ฟŒ๋ฆฝ๋‹ˆ๋‹ค.

๐Ÿงฉ 6๋‹จ๊ณ„ โ€“ ๊ทธ๋ผ๋””์–ธํŠธ์™€ ํ…์Šค์ฒ˜ ๊ฒฐํ•ฉ

def generate_background(size):
    base = generate_gradient(size)
    texture = generate_texture(size)
    base.paste(texture, (0, 0), texture)
    return base

โœ” ๊ทธ๋ผ๋””์–ธํŠธโ€ƒโœ” ๋…ธ์ด์ฆˆโ€ƒโœ” ํ…์Šค์ฒ˜โ€ƒโœ” ์ตœ์ข… ์ด๋ฏธ์ง€

๐Ÿ–ฅ Stepโ€ฏ7 โ€“ Create the Main App Window

class BackgroundGenerator:
    def __init__(self, root):
        self.root = root
        root.title("Background Generator Tool")
        root.geometry("1200x700")
        root.resizable(True, True)

์ด๊ฒƒ์€ ์ฐฝ ์ œ๋ชฉ, ํฌ๊ธฐ ๋ฐ ํฌ๊ธฐ ์กฐ์ • ๊ฐ€๋Šฅ์„ฑ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ Stepโ€ฏ8 โ€“ ๋‚ด๋ณด๋‚ด๊ธฐ ํ•ด์ƒ๋„ ์ •์˜

self.resolutions = {
    "HD (1280x720)": (1280, 720),
    "Full HD (1920x1080)": (1920, 1080),
    "2K (2560x1440)": (2560, 1440),
    "4K (3840x2160)": (3840, 2160),
    "Ultra HD (6000x3375)": (6000, 3375)
}

์‚ฌ์šฉ์ž๋Š” ํ•œ ๋ฒˆ์˜ ํด๋ฆญ์œผ๋กœ ์ „๋ฌธ๊ฐ€ ์ˆ˜์ค€์˜ ํฌ๊ธฐ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐ŸŽš Stepโ€ฏ9 โ€“ Build the Control Panel (Left Side)

left = tb.Frame(root, width=300, bootstyle="secondary")
left.pack(side="left", fill="y", padx=10, pady=10)

Orientation Selector

self.orient_combo = ttk.Combobox(
    left, values=["Landscape", "Portrait"], state="readonly"
)
self.orient_combo.current(0)
self.orient_combo.pack(fill=X)

Resolution Selector

self.res_var = tb.StringVar(value="Full HD (1920x1080)")
tb.OptionMenu(left, self.res_var, *self.resolutions.keys()).pack(fill=X)

๐Ÿ–ผ Stepโ€ฏ10 โ€“ ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์บ”๋ฒ„์Šค

self.canvas = tb.Canvas(root, bg="black", highlightthickness=0)
self.canvas.pack(expand=True, fill="both")

์ด๊ฒƒ์€ ์ƒ์„ฑ๋œ ๋ฐฐ๊ฒฝ์˜ ์Šค์ผ€์ผ๋œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”„ 11๋‹จ๊ณ„ โ€“ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ƒ์„ฑ ๋ฐ ํฌ๊ธฐ ์กฐ์ •

def update_preview(self):
    img = self.state["bg"].copy()
    canvas_w = self.canvas.winfo_width()
    canvas_h = self.canvas.winfo_height()

    if canvas_w > 0 and canvas_h > 0:
        ratio = min(canvas_w / img.width, canvas_h / img.height)
        preview = img.resize(
            (int(img.width * ratio), int(img.height * ratio)),
            Image.ANTIALIAS
        )
        self.tk_img = ImageTk.PhotoImage(preview)
        self.canvas.create_image(
            canvas_w // 2, canvas_h // 2,
            image=self.tk_img, anchor="center"
        )

์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์บ”๋ฒ„์Šค ํฌ๊ธฐ์— ๋น„๋ก€ํ•˜๋„๋ก ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ–ผ๏ธ ๋‹จ๊ณ„โ€ฏ11 (๋Œ€์•ˆ) โ€“ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ‘œ์‹œ

ratio = min(canvas_w / img.width, canvas_h / img.height)
new_size = (int(img.width * ratio), int(img.height * ratio))
preview = img.resize(new_size)

self.tk_img = ImageTk.PhotoImage(preview)
self.canvas.delete("all")
self.canvas.create_image(
    canvas_w // 2, canvas_h // 2, image=self.tk_img
)

๐Ÿง  ์ด๋ฏธ์ง€๋ฅผ ์ค‘์•™์— ์œ ์ง€ํ•˜๊ณ  ์ ์ ˆํžˆ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’พ Stepโ€ฏ12 โ€“ ๋ฐฐ๊ฒฝ ๋‚ด๋ณด๋‚ด๊ธฐ

def save_bg(self):
    w, h = self.state["size"]
    timestamp = int(time.time())
    filename = f"background_{w}x{h}_{timestamp}.png"

    f = filedialog.asksaveasfilename(
        initialfile=filename,
        defaultextension=".png",
        filetypes=[("PNG Image", "*.png"), ("JPEG Image", "*.jpg")]
    )

    if f:
        self.state["bg"].save(f)
        messagebox.showinfo("Success", "Background saved!")

โœ” ์ž๋™ ํŒŒ์ผ๋ช…
โœ” PNG ๋˜๋Š” JPG ์ง€์›

๐Ÿš€ ์•ฑ ์‹คํ–‰

if __name__ == "__main__":
    root = tb.Window(themename="darkly")   # or "flatly" for a light theme
    app = BackgroundGenerator(root)
    root.mainloop()

์ด์ œ ์™„์ „ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฐฐ๊ฒฝ ์ƒ์„ฑ๊ธฐ GUI๊ฐ€ ์ค€๋น„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด GUI๋ฅผ ํ†ตํ•ด ๋‹ค์Œ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ๋ฐฉํ–ฅ ์„ ํƒ (๊ฐ€๋กœ/์„ธ๋กœ)
  • ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋ชฉ๋ก์—์„œ ํ•ด์ƒ๋„ ์„ ํƒ
  • ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ๋˜๋Š” ์‹ค์‹œ๊ฐ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ™•์ธ
  • ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ๋ช…์œผ๋กœ ์ตœ์ข… ์ด๋ฏธ์ง€ ๋‚ด๋ณด๋‚ด๊ธฐ

ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ์•„๋ฆ„๋‹ต๊ณ  ๋ฌด์ž‘์œ„์ ์ธ ๋ฐฐ๊ฒฝ์„ ๋งŒ๋“œ๋Š” ์ฆ๊ฑฐ์›€์„ ๋ˆ„๋ฆฌ์„ธ์š”!

๐Ÿš€ ์ตœ์ข… ์ƒ๊ฐ

์ด ํ”„๋กœ์ ํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ชฉํ‘œ์— ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค:

  • ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์—ฐ์Šต
  • Tkinter GUI ๊ฐœ๋ฐœ ํ•™์Šต
  • ์ฐฝ์˜์ ์ธ Python ๋„๊ตฌ ์ œ์ž‘

๐Ÿ”— Source Code:
https://github.com/rogers-cyber/python-tiny-tools/tree/main/Background_Generator_Tool

์ด ํŠœํ† ๋ฆฌ์–ผ์ด ๋งˆ์Œ์— ๋“œ์…จ๋‹ค๋ฉด, ๋ ˆํฌ์— โญ ๋ณ„ํ‘œ๋ฅผ ๋‹ฌ๊ณ  Dev.to์— ๊ณต์œ ํ•ด ์ฃผ์„ธ์š”!

ํ–‰๋ณตํ•œ ์ฝ”๋”ฉ ๐Ÿ‘จโ€๐Ÿ’ป๐ŸŽจ

Background Generator Demo

Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

Python, Tkinter, MSS๋ฅผ ์‚ฌ์šฉํ•œ ํ™”๋ฉด ์บก์ฒ˜ ๋ฐ ์Šค์ฝ”ํ”„ ๋„๊ตฌ ๋งŒ๋“ค๊ธฐ

์‹ค์‹œ๊ฐ„ ํ™”๋ฉด ์บก์ฒ˜ GUI์™€ ์Šค์ฝ”ํ”„: ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™”๋ฉด์„ ์บก์ฒ˜ํ•˜๊ณ  ๋น„๋””์˜ค ์Šค์ฝ”ํ”„ ๋ฒกํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์ž‘์€ GUI ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Python๊ณผ Tkinter๋กœ ๊ฐ„๋‹จํ•œ ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ ๋งŒ๋“ค๊ธฐ โ€“ FileMate Explorer

๐Ÿ“‚ FileMate Explorer โ€“ ๊ฐ€๋ฒผ์šด ํŒŒ์ด์ฌ ํŒŒ์ผ ๊ด€๋ฆฌ์ž. ์™„์ „ํžˆ ํŒŒ์ด์ฌ์œผ๋กœ ๋งŒ๋“  ๊ฐ€๋ฒผ์šด ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ๋ฅผ ์›ํ•˜์…จ๋‚˜์š”? Tkinter ๊ธฐ๋ฐ˜ FileMate Explorer๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”.