[2주차] 13기 파이썬 코딩 스터디 (코딩 생성을 위한 3단계 방법으로 북마크 프로그램 만들기)_상상러

[2주차] 13기 파이썬 코딩 스터디 (코딩 생성을 위한 3단계 방법으로 북마크 프로그램 만들기)_상상러

안녕하세요. 오늘은 제가 생성형 인공 지능 도구를 통해 어떤식으로 코드를 얻어내고 조금씩 업데이트 해나가는지 공유해드리고자 합니다.

저는 코드 생성을 위해 저만의 방법을 만들었고 조금씩 개선해나가고 있습니다.

총 3단계의 구성이며 세부 내용은 다음과 같습니다.

1단계: 역할 부여

2단계: 아이디어 제안

3단계: 코드 요청

생성형 인공지능 도구는 프롬프트에 분명 민감할 것이기 때문에 저는 위와 같은 방식으로 코드 요청을 합니다. 그럼 실제로 북마크 프로그램을 만드는 실제 사례를 살펴보겠습니다. 원래 반말로 해야 잘된다고 하는데 저는 일단 정중한 태도로 한번 작성해보았습니다.

<1단계 역할 부여>
1) 역할: 당신은 대한민국에서 가장 유명한 파이썬 전문가입니다. 전세계에서도 알아주는 전문가입니다. 당신은 파이썬 코딩 능력 관련해서 뛰어난 능력을 가지고 있습니다.
2) 질문 이유: 코딩과 관련하여 잘 모르는 저에게 당신은 아주 훌륭한 코드를 제공해줄수 있습니다. 당신을 통해 코딩을 배우고 활용하고자 합니다.
3) 말투와 어조: 현명하고 친절한 말투로 부탁합니다. 구체적인 정보가 좋습니다.
4) 양식: 우선 당신은 방법을 저에게 알려주고 그 방법을 코드로 제공을 원하는지 저에게 물어보셔야 합니다. 해당 [아이디어]를 실현시킬 수 있는 적절한 방법을 차근차근 제안해 주세요.
5) 지시문: 나는 당신에게 나의 [아이디어]를 줄 것입니다. 그럼 당신은 나의 [아이디어] 에 알맞게 파이썬 코드를 설계하는 방법을 먼저 알려줍니다. 그 뒤에 저에게 코드를 원하 는지 물어봅니다. 내가 원한다고 답할 때만 코드를 주세요. 만약 코드를 달라는 별도의 말 이 없는 경우는 절대 전체 코드를 주지 마세요

역할 부여는 저만의 프롬프트 5단계 기법으로 한번 작성해보았습니다. 역할, 질문 이유, 말투와 어조, 양식, 지시문의 순서대로 역할을 부여하고 상대방에게 어떤 것을 할 것인지 설명합니다. 마치 스텝 바이 스텝 프롬프트 단계를 밟기 위해 준비하는 단계라고 보면 됩니다.

<2단계 아이디어 제안>
[아이디어] 단축키 기반 북마크 관리 프로그램

1. 목표
사용자가 웹사이트 북마크를 관리하고, 설정한 단축키로 빠르게 웹사이트에 접근할 수 있는 GUI 프로그램을 만듭니다. 특수 키를 포함한 단축키 매핑 기능을 구현하여 직관적인 단축키 관리를 제공합니다.

2. 요구사항
- PySide6를 사용하여 GUI를 구현합니다.
- webbrowser 라이브러리를 사용하며 기본적으로 엣지 브라우저를 사용합니다.
- 사용자가 웹사이트 이름, URL, 단축키를 입력하여 북마크를 추가할 수 있습니다.
- 등록된 북마크들은 gui상에 표시가 되어야 합니다.
- 생성된 각 북마크에는 옆에 버튼이 3개가 있어야 합니다. 실행 버튼, 수정 버튼, 삭제 버튼을 추가해주시고 버튼 클릭시 해당하는 기능이 동작되어야 합니다.
- UI가 사용자 친화적이어야 합니다. 항시 켜두고 실행할 수 있도록 오른쪽 사이드바로서 기능할 수 있도록 설계해주세요
- 항상 위에 체크박스 기능을 넣어주세요
- 카테고리 관리 기능(추가, 삭제, 편집)을 추가합니다. 단축키는 등록할 때 카테고리를 선택할 수 있게 해주어야 합니다. 내가 원하는 카테고리만 골라서 보는 기능이 있어야 합니다.
- 단축키 입력은 QlineEdit를 이용하여 입력받겠습니다. 특수키를 입력할 때 대소문자 구분없이 입력받아도 잘 처리되도록 구현하여 사용자 편의성을 증대시킵니다.
- 사용자가 입력한 단축키를 pynput이 요구하는 형식으로 변환해야 합니다. 이는 Ctrl, Alt, Shift와 같은 특수 키를 <ctrl>, <alt>, <shift>로 변환하는 것이 중요합니다. 윈도우키도 함께 고려해주세요. 이를 위해, 사용자가 입력한 문자열을 분리하고 특수 키에 대해 딕셔너리를 사용하여 적절하게 변환합니다.
- '단축키 등록 중 오류가 발생했습니다: ctrl'와 같은 오류가 나오지 않도록 특수키를 <>로 묶어서 잘 변환해주어야 합니다. 예를 들면 ctrl을 <ctrl> 잘 변환해주어야 합니다.
- 북마크 파일에 만약 'ctrl' 로 저장되어있다면 '<ctrl>'로 자동 인식할 수 있어야 합니다. 물론 <ctrl>로 저장되어 있는 경우도 정상 작동해야 하며 섞여 있는 경우에도 상관없이 잘 동작할 수 있도록 코드를 점검해주세요.
- JSON 파일을 통해 북마크 데이터를 관리합니다. 프로그램이 실행될 때 데이터를 불러오고, 종료 시 저장합니다. JSON 파일이 없는 경우 알아서 파일을 생성해주어야 합니다.
- 단축키 중복을 방지하며, 중복 시 경고 메시지를 표시합니다. 
- 단축키에 특수문자를 잘못입력하는 경우 등록이 되지 않도록 경고메시지를 표시합니다. 예를 들면 ctrl+alt+sifde+e와 같이 shift에서 오타가 난 경우 등록이 되어서는 안됩니다.
- pynput 라이브러리를 사용해 전역 단축키를 관리하고, 단축키를 입력할 때 해당 URL이 실행됩니다. 
- pynput.keyboard.GlobalHotKeys를 사용하도록 하세요
- functools.partial을 활용하여 실행과 삭제 버튼을 구현하세요.
- 단축키 입력은 별도의 쓰레드(threading 라이브러리 활용)에서 관리할 수 있도록 해주세요.
- 모든 코드는 천천히 차근차근 작성하여 모든 기능이 다 담겨야 하며 특히 오류가 발생하지 않도록 작성해주셔야 합니다.

3. 주의사항
-예상되는 문제: pynput의 GlobalHotKeys에서 단축키를 인식할 때 발생하는 문제로, 특수키(Key.ctrl, Key.alt, Key.shift)와 일반 키(예: 'n')를 튜플로 함께 사용하려는 시도에서 문제가 생긴 것입니다. GlobalHotKeys는 특수 키와 일반 키를 적절히 결합한 문자열이나 튜플을 받아야 하며, 지금처럼 키와 문자열을 섞어 사용하는 것은 문제가 됩니다. Key.ctrl 이런 것들은 쓰지 말아주세요.

아이디어 제안은 최대한 상세하게 써보고 중간에 오류를 발견하면 다시 이 친구에게 알려주는 방식으로 하나의 메뉴얼을 만들었습니다. 이렇게 하나 만들어두면 다른 코딩을 해나갈 때 지식 정보로서 활용되기도 하고 해당 프로그램의 질을 높여주는데 기여를 합니다. 처음에는 무지 간단했는데 하다보니까 복잡해졌습니다. 저렇게 긴 경우에는 제대로 이해하지 못할 수 있기 때문에 아이디어 요청 시 분할해서 요청하는 것이 더 효과적일 것으로 예상합니다. 다만 저는 지금까지는 항상 나눠서 보내지 않고 한번에 보냈습니다. 추후에 테스트 해볼 예정입니다.

<3단계 코드 요청>
전체 코드를 주세요

단 아래 9개의 지시문을 반드시 준수해주세요

9개의 지시문을 단계적으로 읽고 이를 고려하여 천천히 차근차근 코드를 생성합니다.

1) 주석을 초등학생도 이해하기 쉽게 자세히 달아주세요. 특히 파이썬 코드의 경우 코드가 잘 실행되기 위해서 어떤 라이브러리를 설치해야 하는지 반드시 알려주세요. 코드의 최상단에 '#pip install 라이브러리1 라이브러리2 라이브러리3' 이런식으로 알려주세요. 기본적으로 설치되어 있는 라이브러리는 알려주지 않아도 됩니다. 그리고 기본 내장되어있는 라이브러리의 경우 import가 잘될 수 있도록 코드 생성시 차근차근 검토바랍니다.

2) 코드에 있어서 오류가 나오지 않도록 천천히 차근차근 작성해주세요.

3) 사용자가 입력해야 하는 것이 있다면 맨 위에 표시해주세요. 변수를 관리하기 쉬워야 합니다. 

4) 저는 코딩 초보라는 걸 명심하세요. 사용자가 쉽게 적용할 수 있는 내용으로 코드를 구성하세요.

5) json파일이나 텍스트 파일, 이미지 파일 등 각종 파일을 참고할 때 실행하고 있는 파이썬 파일의 경로와 똑같은 곳에 있다고 가정해주세요. os 라이브러리를 활용해서 해주세요. json파일이나 텍스트 파일의 경우 해당 파일이 없다면 새로 생성해주세요. 이미지 파일은 그러지 말아주세요.

6) 실행되는 폴더 경로에 한글이 있더라도 오류 없이 잘 실행될 수 있도록 코드 내용을 신경 써 주세요. 예를 들어 파이썬이 실행되는 경로에 파일 이름이 한글로 되어있는 경우 오류가 종종 발생합니다. 따라서 해당 오류가 발생하지 않도록 코드를 생성해주세요. 저는 한글 파일 이름으로 된 파이썬 파일을 만드는 경우가 많습니다. 예를 들면 한글 파일 이름을 '諛뷀깢' 같은 것으로 이상하게 인식하는 경우가 있습니다. 정상적으로 인식 될 수 있도록 해주세요. 한글 파일 경로를 처리할 때 발생하는 문제를 해결하려면, OpenCV와 Python의 파일 경로 처리를 위한 적절한 방법을 사용해야 합니다. 특히, cv2.imread 대신 cv2.imdecode와 np.fromfile을 사용하여 경로에 한글이 포함된 이미지를 읽는 것이 중요합니다.

7) UnicodeDecodeError: 'cp949' codec can't decode byte 0xeb in position 47: illegal multibyte sequence 이러한 오류가 발생하지 않도록 유의합니다. JSON 파일을 UTF-8로 읽도록 수정해야 합니다.

8) 파이썬 실행 시 어디서 오류가 나는지 확인하고자 합니다. 디버그 기능을 추가해주세요. 터미널 창에 현재 어떠한 것이 실행되고 있는지 표시해주시고 오류가 나는 경우에 어떤 오류가 나는 것인지 표시해주세요.

9) 변수가 아직 정의되지 않은 상태에서 변수를 호출하지 마세요.

코드 요청 프롬프트는 마지막 단계로 파이썬에서 예상 되는 오류들을 저 나름대로 정리한 것입니다. 물론 해당 내용은 더 추가될 수 있겠습니다. 이런식으로 제한 조건과 예상되는 문제점을 집어준다면 코드 생성의 질을 높일 수 있습니다.

해당 단계를 바탕으로 저는 o1-mini와 대화를 나누었고 다음과 같은 코드를 얻어내었습니다.

# 필요한 라이브러리 설치 명령 (터미널에서 입력)
# pip install PySide6 pynput

import os
import sys
import json
import webbrowser
from PySide6.QtWidgets import QApplication, QMainWindow, QLineEdit, QPushButton, QVBoxLayout, QWidget, QLabel, QTableWidget, QTableWidgetItem, QMessageBox
from PySide6.QtCore import Qt
from pynput import keyboard
import threading

# 1. JSON 파일 경로 설정 (실행되는 파이썬 파일과 같은 위치)
# 파일 경로에 한글이 있어도 오류 없이 실행되도록 처리합니다.
json_file_path = os.path.join(os.path.dirname(__file__), "bookmarks44.json")

# 2. 북마크 데이터를 저장하는 함수
# JSON 파일이 없으면 새로 생성하고, 기존 파일이 있으면 불러옵니다.
def load_bookmarks():
    if not os.path.exists(json_file_path):
        # 파일이 없으면 빈 리스트로 초기화
        return []
    try:
        with open(json_file_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except (json.JSONDecodeError, UnicodeDecodeError) as e:
        print(f"북마크 데이터를 불러오는 중 오류 발생: {e}")
        return []

# 3. 북마크 데이터를 저장하는 함수
def save_bookmarks(bookmarks):
    try:
        with open(json_file_path, 'w', encoding='utf-8') as f:
            json.dump(bookmarks, f, ensure_ascii=False, indent=4)
    except Exception as e:
        print(f"북마크 데이터를 저장하는 중 오류 발생: {e}")

# 단축키 문자열을 pynput의 GlobalHotKeys 형식에 맞게 변환하는 함수
def normalize_shortcut(shortcut):
    mapping = {
        'ctrl': '<ctrl>',
        'alt': '<alt>',
        'shift': '<shift>',
        'cmd': '<cmd>',
        'win': '<cmd>'
    }
    parts = shortcut.replace(' ', '').split('+')
    normalized_parts = []
    for part in parts:
        key = part.lower()
        if key in mapping:
            normalized_parts.append(mapping[key])
        else:
            normalized_parts.append(key)
    return '+'.join(normalized_parts)

# 4. 북마크 관리 프로그램 GUI 구현
class BookmarkManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("단축키 기반 북마크 관리 프로그램")
        self.setGeometry(300, 100, 600, 400)

        # 북마크 데이터를 로드
        self.bookmarks = load_bookmarks()

        # 핫키 매니저 초기화
        self.hotkey_manager = HotkeyManager()

        # GUI 요소 생성
        self.name_input = QLineEdit(self)
        self.name_input.setPlaceholderText("웹사이트 이름")

        self.url_input = QLineEdit(self)
        self.url_input.setPlaceholderText("웹사이트 URL")

        self.shortcut_input = QLineEdit(self)
        self.shortcut_input.setPlaceholderText("단축키 입력 (예: Ctrl+Alt+1)")

        self.add_button = QPushButton("북마크 추가", self)
        self.add_button.clicked.connect(self.add_bookmark)

        self.table = QTableWidget(self)
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(["이름", "URL", "단축키"])
        self.update_table()

        # 레이아웃 설정
        layout = QVBoxLayout()
        layout.addWidget(QLabel("북마크 이름"))
        layout.addWidget(self.name_input)
        layout.addWidget(QLabel("URL"))
        layout.addWidget(self.url_input)
        layout.addWidget(QLabel("단축키"))
        layout.addWidget(self.shortcut_input)
        layout.addWidget(self.add_button)
        layout.addWidget(self.table)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        # 기존 북마크의 단축키 등록
        for bookmark in self.bookmarks:
            shortcut_normalized = normalize_shortcut(bookmark['shortcut'])
            def callback(b=bookmark):
                on_shortcut(b)
            self.hotkey_manager.add_hotkey(shortcut_normalized, callback)

    # 5. 북마크 추가 기능
    def add_bookmark(self):
        name = self.name_input.text().strip()
        url = self.url_input.text().strip()
        shortcut = self.shortcut_input.text().strip()

        # 입력값이 유효한지 확인 (빈 값 또는 잘못된 형식 방지)
        if not name or not url or not shortcut:
            QMessageBox.warning(self, "입력 오류", "모든 필드를 올바르게 입력해주세요!")
            return

        # 단축키 중복 체크
        if any(b['shortcut'] == shortcut for b in self.bookmarks):
            QMessageBox.warning(self, "중복 오류", "이미 사용 중인 단축키입니다!")
            return

        # 북마크 추가 후 테이블 업데이트
        self.bookmarks.append({"name": name, "url": url, "shortcut": shortcut})
        save_bookmarks(self.bookmarks)
        self.update_table()

        # 단축키 등록
        shortcut_normalized = normalize_shortcut(shortcut)
        def callback(b=self.bookmarks[-1]):
            on_shortcut(b)
        self.hotkey_manager.add_hotkey(shortcut_normalized, callback)

    # 6. 테이블 업데이트 기능
    def update_table(self):
        self.table.setRowCount(len(self.bookmarks))
        for i, bookmark in enumerate(self.bookmarks):
            self.table.setItem(i, 0, QTableWidgetItem(bookmark["name"]))
            self.table.setItem(i, 1, QTableWidgetItem(bookmark["url"]))
            self.table.setItem(i, 2, QTableWidgetItem(bookmark["shortcut"]))

# 7. 전역 단축키 처리 함수 수정
def on_shortcut(bookmark):
    print(f"{bookmark['shortcut']} 단축키로 {bookmark['url']}을(를) 엽니다.")
    # 기본 웹 브라우저로 URL 열기
    webbrowser.open(bookmark["url"])

# 핫키 매니저 클래스 정의
class HotkeyManager:
    def __init__(self):
        self.hotkey_listener = None
        self.hotkeys = {}
        self.lock = threading.Lock()
        self.running = True
        self.thread = threading.Thread(target=self.run)
        self.thread.daemon = True  # 프로그램 종료 시 같이 종료되도록 설정
        self.thread.start()

    def add_hotkey(self, shortcut, callback):
        with self.lock:
            self.hotkeys[shortcut] = callback
            self.update_listener()

    def remove_hotkey(self, shortcut):
        with self.lock:
            if shortcut in self.hotkeys:
                del self.hotkeys[shortcut]
                self.update_listener()

    def update_listener(self):
        if self.hotkey_listener:
            self.hotkey_listener.stop()
            self.hotkey_listener = None

        if self.hotkeys:
            self.hotkey_listener = keyboard.GlobalHotKeys(self.hotkeys)
            self.hotkey_listener.start()

    def stop(self):
        self.running = False
        if self.hotkey_listener:
            self.hotkey_listener.stop()
        self.thread.join()

    def run(self):
        while self.running:
            # 스레드를 계속 유지
            threading.Event().wait(1)

# 10. 프로그램 실행
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = BookmarkManager()
    window.show()
    sys.exit(app.exec())

몇개 놓친 것도 있지만 나름대로 만족할만한 결과를 얻었습니다. 실행 버튼을 만드는 것을 놓쳤네요. 그리고 삭제 버튼까지도요! 다만 수정 기능은 더블 클릭해서 수정할 수 있었습니다.

한국어 텍스트가 표시된 컴퓨터 화면의 스크린샷

놓친 것이 있다면 재요청만 하면 됩니다!

저는 이 프로그램에서 조금씩 업데이트를 거쳐서 이제 마우스 커서를 가져다대면 열리는 북마크 사이드바를 만들고 있습니다.

어도비 어도비 어도비 어도비 어도비의 스크린샷

제가 위에서 공유드린 단계는 초안을 잡을 때 활용하시면 좋을 것 같습니다.

생성형 인공지능 도구의 유용함을 다시 한번 느낀 한 주 였습니다.

이상 사례 공유 마치겠습니다!

3
1개의 답글

👉 이 게시글도 읽어보세요