본문 바로가기

MLOps/Development

Pyside6로 만든 EXE 파일을 수정하다 알게 된 것들

Pyside6로 만든 EXE 파일을 수정하다 알게 된 것들


최근에 Pyside6을 가지고 프로그램을 만들어서 지인에게 제공했다

 

쿠팡 상품 정보 수집기 -> 오픈 마켓 상품 정보 수집기

쿠팡 상품 정보 수집기 -> 오픈 마켓 상품 정보 수집기 로그인 로그인 기능 추가 DataBase는 Ncloud의 CloudDB를 사용 중 2. 카테고리 및 연관 키워드 수집 사용자의 엑셀 데이터를 입력받고 오픈 마켓별

junnyhi.tistory.com


하루뒤에 피드백이 왔는데 버그가 엄청 많았다.

그래서  버그들을 수정하면서 알게 된 것들을 기록으로 남기려고 한다.

<김버그씨와의 대화>


1. Lazy initialization

Pyside6을 사용해 EXE 파일을 만들고 실행하는 경우, 로드시간이 생각보다 오래 걸렸다

따라서 특정 객체의 초기화 시점을 필요한 순간에 초기화하는 과정이 필요했다. (근데 지금도 많이 걸린다)

 

예를 들어서 로그인이 성공한 경우,

다음 작업에 필요한 객체를 초기화해야 했다. 로그인이 성공도 안 했는데 해당 객체를 초기화할 필요가 없으니까..

    msgBox = QMessageBox()
    msgBox.setText("로그인 성공!")
    msgBox.exec()
    self.accept()
    <특정 객체 초기화>

 

Lazy initializaion

객체 초기화 시점을 실제로 그것이 필요할 때까지 미루는 방식

이 방식은 계산 비용이 큰 객체를 초기화하거나, 특정 조건이 충족될 때까지 초기화를 미룰 때 유용

 

아래 방식은 object property에 접근할 때(필요할 때) HeavyObject가 초기화된다

class HeavyObject:
    def __init__(self):
        print("Creating HeavyObject")

class LazyInitialization:
    def __init__(self):
        self._object = None

    @property
    def object(self):
        if self._object is None:
            self._object = HeavyObject()
        return self._object

2. Flat is better than nested!!

이것은 파이썬의 설계 원칙 "The Zen of Python(PEP 20)' 원칙 중 하나인데, 

구조를 중첩 없이 평평하게 유지해서 간결하고 이해하기 쉽게 유지하라는 말인 것 같다.

 

PEP 20 – The Zen of Python | peps.python.org

PEP 20 – The Zen of Python Author: Tim Peters Status: Active Type: Informational Created: 19-Aug-2004 Post-History: 22-Aug-2004 Table of Contents Long time Pythoneer Tim Peters succinctly channels the BDFL’s guiding principles for Python’s design int

peps.python.org

 

버그에 막혀 줄줄줄 코드를 작성하던 도 중, 개판 나있는 코드 상태를 보고 정신 차리고 수정을 하던 도 중 알게 된 문구다

 

아래는 과도한 중첩이 된 코드이다.  (귓방맹이나 안 맞으면 다행인 코드라고 볼 수 있다)

def overly_nested(list_of_lists):
    for i in range(len(list_of_lists)):
        for j in range(len(list_of_lists[i])):
            if list_of_lists[i][j] == 'bad':
                for k in range(j+1, len(list_of_lists[i])):
                    if list_of_lists[i][k] != 'bad':
                        return False
    return True

 

아래는 더 평평하게 리팩터링 된 예시이다

def flat(list_of_lists):
    for sublist in list_of_lists:
        try:
            bad_index = sublist.index('bad')
            if 'bad' in sublist[bad_index + 1:]:
                return False
        except ValueError:
            continue
    return True

3. Inno Setup

EXE 설치를 위한 설치 파일을 만들어서 제공하기 위해 Inno Setup을 사용했다.

프로그램 로드 시간을 줄이기 위해 Pyinstaller를 사용할 때, onefile 옵션을 사용하지 않았다.

# pyinstaller --onefile main.py
pyinsatller main.py

onefile 옵션을 사용하면 단일 exe 파일이 생성되는데, 

이것이 실행될 때 일시적으로 필요한 파일들이 디스크 상의 임시 위치에 압축 해제가 된다.

압축 해제 과정은 추가 시간을 필요로 하기 때문에, 사용하지 않았을 때보다 다소 길어질 수 있다.

 

하지만 해당 옵션을 사용하지 않고 사용자에게 프로그램을 전달하려다 보니,

여러 파일들이 보기 좀 불편해서 Installer를 만들어서 제공했다. (압축해서 덩그러니 줄려니 전문성도 좀 떨어져 보이기도..)