강의

멘토링

로드맵

인프런 커뮤니티 질문&답변

ssaycao님의 프로필 이미지
ssaycao

작성한 질문수

직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피

독립형 프로그램으로 추출 후 오류 발생

작성

·

124

1

질문 남겨주셔서 감사합니다.

막히면 언제든 무엇이든 자주 질문 던져주세요.

수학/과학이나 알고리즘과는 달리

업무자동화 코딩은 고민해서 풀리는 경우가 정말 드뭅니다.

다시 말씀드리지만, 질문을 자주자주 남겨주세요.

저도 최대한 빠르게 회신 드리겠습니다.

당부드릴 두 가지가 있습니다.

① 가급적 구체적으로 설명해주세요.
② 특정 챕터 관련 질문 남겨주실 때는,
어느 챕터인지 알려주세요ㅜ

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

 

 안녕하세요, 오랜만입니다.

저번에도 한번 여쭤본 적이 있는데, 해결 못하고 방치해두고 있다가 다시 시간이 나서 재도전중인 문제입니다.

 

제가 만든 코드는 pyhwpx를 이용해 페이지를 자동으로 매기는 프로그램입니다.

코드 자체는 정상적으로 작동하는 것을 확인했습니다.

문제는 제가 이걸 회사 사람들과 공유해서 쓰기 위해 별도의 독립형 프로그램으로 추출할 때 생깁니다.

제 목표는 컴퓨터에 파이썬도, pyhwpx도 설치되어 있지 않은

그런 사람들의 컴퓨터에서 실행을 해도 정상 작동하는 것인데요.

 

그런데 그런 사람의 컴퓨터에서 실행을 하면 아래와 같은 오류가 뜨고

image.png

오류를 구체적으로 확인해보려 해도 파이썬이 없는 컴퓨터라 확인할 수가 없네요.

chatGPT한테 물어보니 다른 사람 컴퓨터에서 pyhwpx가 한글 파일을 찾지 못하는 문제라고 하는데

제가 Hwp.exe의 경로를 찾아서 넣어주기까지 했는데도 같은 오류가 발생하네요.

뭐가 문제인지 알 수 있을까요?

꼭 좀 부탁드립니다 ㅠㅠ.

 

제가 작성한 코드는 다음과 같습니다.

import tkinter as tk
from tkinter import filedialog, messagebox
from pyhwpx import Hwp
import os

# 한글 경로 탐색 함수
def find_hwp_path():
    # 명시적인 경로 설정
    possible_paths = [
        r"C:\\Program Files (x86)\\Hnc\\Office 2022\\HOffice120\\Bin\\Hwp.exe",  # Office 2022 기본 경로
        r"C:\\Program Files\\Hnc\\Office 2020\\Hwp.exe",                       # Office 2020
        r"C:\\Program Files (x86)\\Hancom\\Office 2018\\Hwp.exe",              # Office 2018
        r"C:\\Program Files (x86)\\HWP\\Bin\\Hwp.exe"                         # 과거 한글
    ]

    # 경로 확인
    for path in possible_paths:
        if os.path.exists(path):
            return path

    # 경로를 찾지 못하면 None 반환
    return None

# 한글 연동 함수
def set_page_numbers(file_paths, odd_files):
    try:
        hwp_path = find_hwp_path()
        if not hwp_path:
            messagebox.showerror("오류", "한글 경로를 찾을 수 없습니다.")
            return

        # 한글 프로그램 객체 생성
        hwp = Hwp(hwp_path)
        hwp.XHwpWindows.Item(0).Visible = False  # 한글 창 숨기기

        j = 1  # 페이지 번호 초기값
        for file_path in file_paths:
            hwp.Open(file_path)

            # 첫 페이지 이동
            hwp.HAction.Run("MoveTopLevelBegin")

            # 홀수 페이지로 시작하지 않는 파일 처리
            if file_path in odd_files:
                hwp.PageNumPos(j)
            else:
                if j % 2 == 0:  # 기본적으로 홀수 페이지 시작
                    j += 1
                hwp.PageNumPos(j)

            # 페이지 수 증가 (현재 문서 페이지 수만큼 더함)
            j += hwp.PageCount

            hwp.Clear(2)

        hwp.XHwpWindows.Item(0).Visible = True  # 작업 후 한글 창 보이기
        hwp.Clear(3)
        hwp.Quit()

        messagebox.showinfo("완료", "페이지 번호 작업이 완료되었습니다!")

    except Exception as e:
        import traceback
        error_message = traceback.format_exc()
        messagebox.showerror("오류", f"작업 중 오류가 발생했습니다:\n{error_message}")


# 파일 이동 함수
def move_up(listbox):
    selected_items = listbox.curselection()
    if not selected_items:
        return
    for index in selected_items:
        if index > 0:  # 첫 번째 항목이 아닌 경우에만 이동 가능
            value = listbox.get(index)
            listbox.delete(index)
            listbox.insert(index - 1, value)
            listbox.selection_set(index - 1)

def move_down(listbox):
    selected_items = listbox.curselection()
    if not selected_items:
        return
    for index in reversed(selected_items):
        if index < listbox.size() - 1:  # 마지막 항목이 아닌 경우에만 이동 가능
            value = listbox.get(index)
            listbox.delete(index)
            listbox.insert(index + 1, value)
            listbox.selection_set(index + 1)

# 파일 추가 및 제거 함수
def add_file_to_list(listbox):
    files = filedialog.askopenfilenames(
        title="파일을 선택하세요",
        filetypes=[("HWP/HWPX 파일", "*.hwp;*.hwpx"), ("HWP 파일", "*.hwp"), ("HWPX 파일", "*.hwpx"), ("모든 파일", "*.*")]
    )
    for file in files:
        listbox.insert(tk.END, file)

def remove_selected_file(listbox):
    selected_items = listbox.curselection()
    for item in reversed(selected_items):
        listbox.delete(item)

# 실행 함수
def execute_task():
    all_files = list(listbox_all_files.get(0, tk.END))
    odd_files = list(listbox_odd_files.get(0, tk.END))

    if not all_files:
        messagebox.showerror("오류", "전체 파일 목록이 비어 있습니다.")
        return

    set_page_numbers(all_files, odd_files)

# GUI 설정
root = tk.Tk()
root.title("페이지 번호 매기기 프로그램")
root.geometry("800x650")
root.resizable(False, False)

# 전체 파일 프레임
frame_all_files = tk.Frame(root, relief="solid", bd=1)
frame_all_files.place(x=10, y=10, width=600, height=250)

label_all_files = tk.Label(frame_all_files, text="전체 파일", font=("맑은 고딕", 12, "bold"))
label_all_files.pack(anchor="nw", padx=5, pady=5)

listbox_all_files = tk.Listbox(frame_all_files, selectmode="extended")
listbox_all_files.pack(fill="both", expand=True, padx=5, pady=5)

btn_frame_all_files = tk.Frame(root)
btn_frame_all_files.place(x=620, y=10, width=160, height=250)

btn_add_file = tk.Button(btn_frame_all_files, text="파일 추가", command=lambda: add_file_to_list(listbox_all_files))
btn_add_file.pack(fill="x", padx=5, pady=5)

btn_remove_file = tk.Button(btn_frame_all_files, text="선택파일 삭제", command=lambda: remove_selected_file(listbox_all_files))
btn_remove_file.pack(fill="x", padx=5, pady=5)

btn_move_up = tk.Button(btn_frame_all_files, text="위로 이동", command=lambda: move_up(listbox_all_files))
btn_move_up.pack(fill="x", padx=5, pady=5)

btn_move_down = tk.Button(btn_frame_all_files, text="아래로 이동", command=lambda: move_down(listbox_all_files))
btn_move_down.pack(fill="x", padx=5, pady=5)

# 홀수 페이지로 시작하지 않는 파일 프레임
frame_odd_files = tk.Frame(root, relief="solid", bd=1)
frame_odd_files.place(x=10, y=280, width=600, height=250)

label_odd_files = tk.Label(frame_odd_files, text="홀수 페이지로 시작하지 않는 파일", font=("맑은 고딕", 12, "bold"))
label_odd_files.pack(anchor="nw", padx=5, pady=5)

listbox_odd_files = tk.Listbox(frame_odd_files, selectmode="extended")
listbox_odd_files.pack(fill="both", expand=True, padx=5, pady=5)

btn_frame_odd_files = tk.Frame(root)
btn_frame_odd_files.place(x=620, y=280, width=160, height=250)

btn_add_odd_file = tk.Button(btn_frame_odd_files, text="파일 추가", command=lambda: add_file_to_list(listbox_odd_files))
btn_add_odd_file.pack(fill="x", padx=5, pady=5)

btn_remove_odd_file = tk.Button(btn_frame_odd_files, text="선택파일 삭제", command=lambda: remove_selected_file(listbox_odd_files))
btn_remove_odd_file.pack(fill="x", padx=5, pady=5)

btn_move_up_odd = tk.Button(btn_frame_odd_files, text="위로 이동", command=lambda: move_up(listbox_odd_files))
btn_move_up_odd.pack(fill="x", padx=5, pady=5)

btn_move_down_odd = tk.Button(btn_frame_odd_files, text="아래로 이동", command=lambda: move_down(listbox_odd_files))
btn_move_down_odd.pack(fill="x", padx=5, pady=5)

# 실행 버튼
btn_execute = tk.Button(root, text="실행", command=execute_task, font=("맑은 고딕", 14, "bold"), bg="#00484D", fg="white")
btn_execute.place(x=10, y=550, width=770, height=40)

root.mainloop()

그리고 제가 추출하는데 사용한 것은

pyinstaller --onefile --noconsole --icon="C:\\Users\\user\\Desktop\\@공유\\JINA\\0__exe\\페이지 자동 맞춤 프로그램\\icon.ico"  --hidden-import=pyhwpx --hidden-import=subprocess page_numbering_v2.py

이거에요. 뭐가 문제일까요?

 

따뜻한 연말 되시기 바라며,

감사합니다.

 

답변 2

1

일코님의 프로필 이미지
일코
지식공유자

ssaycao님 안녕하세요?

보안모듈(FilePathCheckerModule.dll) 관련 오류로 보입니다ㅜ

(얼마 전에 이와 관련한 업데이트를 한 번 하기는 했는데, 완전히 해결되지 않은 듯 합니다.)

 

우선 pyhwpx 모듈을 업데이트해주시고, 

프로그램을 배포하실 때, 두 가지만 테스트 부탁드립니다. (아마 둘 중 하나로는 해결될것입니다.)

① 컴파일하실 때, FilePathCheckerModule.dll 파일을 작업폴더에 넣은 상태에서

--add-binary="FilePathCheckerModule.dll:."

옵션을 하나 더 추가하여 컴파일해보시겠어요?

 

② 만약 위의 방법으로도 해결되지 않는다면ㅜ

FilePathCheckerModule.dll 파일을 exe파일과 같은 폴더에 두고 실행해보시면 해결될 듯 합니다.

 

가급적 pyhwpx 최신 업데이트는 조심스럽게...
현재 버전이 몇인지 기억해 두셨다가, 기존 코드에 오류가 발생하면
롤백을 해주셔야 할 수도 있습니다...ㅜ
(죄송한 말씀이지만, 한/글이 버전업되면서 메서드 일부에서 오류들이 생기는 듯 합니다ㅜ)
대신 제게 증상과 작성하셨던 코드를 알려주시면 조치방법을 빠르게 알려드리겠습니다.

위의 두 가지 방법 중 하나로 공유해보시고,
여전히 위 오류가 발생하면(안 생기기를 간절히 빕니다...)
댓글이나 메일로 알려주시기 바랍니다.

 

죄송한 마음이 큽니다.
부디 잘 해결되길

 

행복한 하루 되세요!!!

ssaycao님의 프로필 이미지
ssaycao
질문자

pyinstaller --onefile --noconsole --icon="C:\\Users\\user\\Desktop\\@공유\\JINA\\0__exe\\페이지 자동 맞춤 프로그램\\icon.ico"  --hidden-import=pyhwpx --hidden-import=subprocess page_numbering_v2.py --add-binary="FilePathCheckerModule.dll:."
  1. pyhwpx 업데이트 (0.41.4로 업데이트)

  2. 위 명령어로 컴파일

  3. 모듈 작업폴더랑 exe 폴더에 둘 다 넣어놓음

  4. 관리자 권한으로 실행

     

     

    다 했는데 같은 오류가 뜨네요,, 왜 이럴까요? ㅠㅠ

     

    image.png

     

ssaycao님의 프로필 이미지
ssaycao
질문자

메일로 파일을 보내드리면 도움이 될까요?

일코님의 프로필 이미지
일코
지식공유자

파일의 문제일 가능성은 거의 없어 보입니다...

항상 보안모듈이 말썽입니다ㅜ

코드를 아래처럼 수정해서 컴파일해주시고,
다소 번거로우시겠지만 "모두 허용" 한 번만 클릭하시도록 안내 부탁드립니다..

 

import tkinter as tk
from tkinter import filedialog, messagebox
from pyhwpx import Hwp
import os

# 한글 경로 탐색 함수
def find_hwp_path():
    # 명시적인 경로 설정
    possible_paths = [
        r"C:\\Program Files (x86)\\Hnc\\Office 2022\\HOffice120\\Bin\\Hwp.exe",  # Office 2022 기본 경로
        r"C:\\Program Files\\Hnc\\Office 2020\\Hwp.exe",                       # Office 2020
        r"C:\\Program Files (x86)\\Hancom\\Office 2018\\Hwp.exe",              # Office 2018
        r"C:\\Program Files (x86)\\HWP\\Bin\\Hwp.exe"                         # 과거 한글
    ]

    # 경로 확인
    for path in possible_paths:
        if os.path.exists(path):
            return path

    # 경로를 찾지 못하면 None 반환
    return None

# 한글 연동 함수
def set_page_numbers(file_paths, odd_files):
    try:
        hwp_path = find_hwp_path()
        if not hwp_path:
            messagebox.showerror("오류", "한글 경로를 찾을 수 없습니다.")
            return

        # 한글 프로그램 객체 생성
        hwp = Hwp(register_module=False, visible=False)

        j = 1  # 페이지 번호 초기값
        for file_path in file_paths:
            hwp.Open(file_path)

            # 첫 페이지 이동
            hwp.HAction.Run("MoveTopLevelBegin")

            # 홀수 페이지로 시작하지 않는 파일 처리
            if file_path in odd_files:
                hwp.PageNumPos(j)
            else:
                if j % 2 == 0:  # 기본적으로 홀수 페이지 시작
                    j += 1
                hwp.PageNumPos(j)

            # 페이지 수 증가 (현재 문서 페이지 수만큼 더함)
            j += hwp.PageCount

            hwp.Clear(2)

        hwp.set_visible = True  # 작업 후 한글 창 보이기
        hwp.Clear(3)
        hwp.Quit()

        messagebox.showinfo("완료", "페이지 번호 작업이 완료되었습니다!")

    except Exception as e:
        import traceback
        error_message = traceback.format_exc()
        messagebox.showerror("오류", f"작업 중 오류가 발생했습니다:\n{error_message}")


# 파일 이동 함수
def move_up(listbox):
    selected_items = listbox.curselection()
    if not selected_items:
        return
    for index in selected_items:
        if index > 0:  # 첫 번째 항목이 아닌 경우에만 이동 가능
            value = listbox.get(index)
            listbox.delete(index)
            listbox.insert(index - 1, value)
            listbox.selection_set(index - 1)

def move_down(listbox):
    selected_items = listbox.curselection()
    if not selected_items:
        return
    for index in reversed(selected_items):
        if index < listbox.size() - 1:  # 마지막 항목이 아닌 경우에만 이동 가능
            value = listbox.get(index)
            listbox.delete(index)
            listbox.insert(index + 1, value)
            listbox.selection_set(index + 1)

# 파일 추가 및 제거 함수
def add_file_to_list(listbox):
    files = filedialog.askopenfilenames(
        title="파일을 선택하세요",
        filetypes=[("HWP/HWPX 파일", "*.hwp;*.hwpx"), ("HWP 파일", "*.hwp"), ("HWPX 파일", "*.hwpx"), ("모든 파일", "*.*")]
    )
    for file in files:
        listbox.insert(tk.END, file)

def remove_selected_file(listbox):
    selected_items = listbox.curselection()
    for item in reversed(selected_items):
        listbox.delete(item)

# 실행 함수
def execute_task():
    all_files = list(listbox_all_files.get(0, tk.END))
    odd_files = list(listbox_odd_files.get(0, tk.END))

    if not all_files:
        messagebox.showerror("오류", "전체 파일 목록이 비어 있습니다.")
        return

    set_page_numbers(all_files, odd_files)

# GUI 설정
root = tk.Tk()
root.title("페이지 번호 매기기 프로그램")
root.geometry("800x650")
root.resizable(False, False)

# 전체 파일 프레임
frame_all_files = tk.Frame(root, relief="solid", bd=1)
frame_all_files.place(x=10, y=10, width=600, height=250)

label_all_files = tk.Label(frame_all_files, text="전체 파일", font=("맑은 고딕", 12, "bold"))
label_all_files.pack(anchor="nw", padx=5, pady=5)

listbox_all_files = tk.Listbox(frame_all_files, selectmode="extended")
listbox_all_files.pack(fill="both", expand=True, padx=5, pady=5)

btn_frame_all_files = tk.Frame(root)
btn_frame_all_files.place(x=620, y=10, width=160, height=250)

btn_add_file = tk.Button(btn_frame_all_files, text="파일 추가", command=lambda: add_file_to_list(listbox_all_files))
btn_add_file.pack(fill="x", padx=5, pady=5)

btn_remove_file = tk.Button(btn_frame_all_files, text="선택파일 삭제", command=lambda: remove_selected_file(listbox_all_files))
btn_remove_file.pack(fill="x", padx=5, pady=5)

btn_move_up = tk.Button(btn_frame_all_files, text="위로 이동", command=lambda: move_up(listbox_all_files))
btn_move_up.pack(fill="x", padx=5, pady=5)

btn_move_down = tk.Button(btn_frame_all_files, text="아래로 이동", command=lambda: move_down(listbox_all_files))
btn_move_down.pack(fill="x", padx=5, pady=5)

# 홀수 페이지로 시작하지 않는 파일 프레임
frame_odd_files = tk.Frame(root, relief="solid", bd=1)
frame_odd_files.place(x=10, y=280, width=600, height=250)

label_odd_files = tk.Label(frame_odd_files, text="홀수 페이지로 시작하지 않는 파일", font=("맑은 고딕", 12, "bold"))
label_odd_files.pack(anchor="nw", padx=5, pady=5)

listbox_odd_files = tk.Listbox(frame_odd_files, selectmode="extended")
listbox_odd_files.pack(fill="both", expand=True, padx=5, pady=5)

btn_frame_odd_files = tk.Frame(root)
btn_frame_odd_files.place(x=620, y=280, width=160, height=250)

btn_add_odd_file = tk.Button(btn_frame_odd_files, text="파일 추가", command=lambda: add_file_to_list(listbox_odd_files))
btn_add_odd_file.pack(fill="x", padx=5, pady=5)

btn_remove_odd_file = tk.Button(btn_frame_odd_files, text="선택파일 삭제", command=lambda: remove_selected_file(listbox_odd_files))
btn_remove_odd_file.pack(fill="x", padx=5, pady=5)

btn_move_up_odd = tk.Button(btn_frame_odd_files, text="위로 이동", command=lambda: move_up(listbox_odd_files))
btn_move_up_odd.pack(fill="x", padx=5, pady=5)

btn_move_down_odd = tk.Button(btn_frame_odd_files, text="아래로 이동", command=lambda: move_down(listbox_odd_files))
btn_move_down_odd.pack(fill="x", padx=5, pady=5)

# 실행 버튼
btn_execute = tk.Button(root, text="실행", command=execute_task, font=("맑은 고딕", 14, "bold"), bg="#00484D", fg="white")
btn_execute.place(x=10, y=550, width=770, height=40)

root.mainloop()

수정부분은 33번 라인,

hwp = Hwp(register_module=False) 입니다.

제 PC에서 되면 다른 분들 PC에서도 다 되는 줄 알고
제가 테스트를 소홀히 했습니다ㅜ

 

(그리고 굳이 오류가 나지 않는다면,--hidden-import 옵션들은 필요가 없어 보이는데요. 혹시 hidden-import를 추가하신 이유가 있었는지요?)

 

코드도 엄청 간결하고, GUI도 깔끔하고
훌륭한 프로그램 넘 잘 짜셨네요!!!

 

보안모듈 문제 업데이트 되면
댓글로든 공지로든 다시 알려드리겠습니다.

 

행복한 하루 되세요!!!

ssaycao님의 프로필 이미지
ssaycao
질문자

와! 그래도 프로그램이 돌아가는게 어디인가요! 해결해주셔서 감사합니다!

hidden import는 그냥,, pyhwpx가 계속 오류가 나길래 무지성으로 한번 해본 거에요 아무 의미도 없었지만,,,ㅎㅎ,,,

보안모듈 업데이트 기다리고 있겠습니다. 감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다.

ssaycao님께서 언급한 문제는 타인의 컴퓨터에서 독립형 파이썬 프로그램을 실행할 때 발생하는 에러로, 주로 원인이 될 수 있는 부분은 여러 가지가 있습니다. 이러한 경우, 파이썬 모듈이나 한글 관련 프로그램이 설치되지 않은 환경에서도 작동 가능한 프로그램을 제작하는 것이 목표입니다.

문제 해결을 위한 접근 방법:

  1. PyInstaller 설정 확인:

    • pyhwpxsubprocess 모듈을 잘 포함하도록 --hidden-import 옵션을 사용하셨지만, pyhwpx 모듈이 실행 환경에서 필요로 하는 모든 리소스를 컴파일 시 완전히 포함했는지 재확인해 보세요.
    • --hidden-import 옵션 외에도 필요한 파일들이 컴퓨터에 제대로 복사되었는지도 점검해 보시기 바랍니다.
  2. 한글 프로그램 경로 문제:

    • 사용 중인 모든 컴퓨터에서 한글 프로그램의 설치 경로가 적절히 설정되어 있는지 확인해 주세요. 특히, 설정한 경로들이 모든 환경에서 일관적으로 존재하는지도 점검해 보세요.
  3. 파일 경로 및 권한 문제:

    • 파이썬 코드가 파일을 찾거나 실행할 수 있는지 확인하기 위해 각 경로에 대해 접근 권한이 충분한지 확인해야 합니다.
  4. 추가 디버깅:

    • 배포할 때 발생할 수 있는 에러를 미리 디버깅하기 위해, 코드에 자세한 에러 로깅을 추가하는 것도 좋은 방법입니다. 이러면 오류가 발생했을 때 정확한 위치와 상태를 기록할 수 있습니다.
  5. 환경 테스트:

    • 다양한 컴퓨터나 환경에서 독립형 파일을 테스트하여 문제가 발생하지 않는지를 반복적으로 확인해 보는 것도 필요합니다.

관련된 질문과 해결책에 대한 추가 정보가 필요하다면 아래 게시물을 참고하시면 도움이 될 수 있습니다:

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

ssaycao님의 프로필 이미지
ssaycao

작성한 질문수

질문하기