[토이 프로젝트] 자동 갱신 게임 명부 제작(7) - 코드 유지 보수 하기

chaejm55·2022년 9월 5일
0

0. 이번 단계 진행 아이디어

이메일로 받는 로그 등을 활용하여 코드 유지 보수를 해보자!😀

1. 이메일로 받은 오류 로그들

약 2년 간 매일 갱신 프로그램을 돌리면서 발생했던 오류들을 바탕으로 코드를 수정한 기록입니다.

1) NoAlertPresentException

alert 발생 시(재갱신 요청 또는 없는 계정)에 alert 창을 닫는 Alert(driver).accept()에서 발생한 예외입니다.
alert 발생 시에만 alert 창을 닫도록 했기 때문에 예외가 발생하면 안 되지만 alert 로딩 속도 등의 문제로 발생하는 것으로 보였습니다.
alert 로딩까지 wait 하는 방법도 있지만 이것 또한 가끔 예외가 발생하여 위의 코드 실행 시 NoAlertPresentException을 예외 처리 해주었습니다.


...

def main():
	...
        try:
            update_status = WebDriverWait(driver, TIMEOUT).until(
                EC.text_to_be_present_in_element((By.XPATH, '//*[@id="btn-sync"]/span'), "최신정보")
            )
        except TimeoutException:
            print("갱신 대기 시간이 초과 되었습니다.")
        except UnexpectedAlertPresentException:
            print("Alert")
            try:
            	Alert(driver).accept()
        	except NoAlertPresentException:  # alert 닫기 오류 핸들링
            	print("alert 창 닫기 오류가 발생했습니다.")
        except AttributeError as e:
            print("웹 페이지 로딩이 완료되지 않았거나 일시적인 오류입니다.")
        except WebDriverException:
            print("갱신 중 오류가 발생했습니다.")
    	time.sleep(3)
	...

2) TabError

윈도우에서 IDE를 통해 작성한 코드를 리눅스에서 vim으로 다시 수정했다가 발생했던 오류입니다.
링크에 당시 상황을 포스팅 해두었습니다.

2. 코드 추가 및 리팩터링

1) 갱신 실패 시 재시도

지금까지의 코드에서는 모든 캐릭터에 대해 한 번만 갱신을 시도하므로 실패 시 갱신 없이 프로그램이 종료됩니다.
갱신 리스트를 통해 성공 시 리스트에서 캐릭터 삭제 & 캐릭터 리스트가 빈 리스트가 될 때까지 갱신을 반복하도록 했습니다.

...
def main():
...
	character_list = worksheet.col_values(1)[1:]  # '캐릭터명' 빼고 리스트 구성
	driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

	while len(character_list) > 0:  # 갱신 캐릭터 리스트가 모두 없어질 때까지 반복(성공 시 리스트에서 캐릭터 삭제)
		for character in character_list:
        	...
            try:
            	update_status = WebDriverWait(driver, TIMEOUT).until(
                EC.text_to_be_present_in_element((By.XPATH, '//*[@id="btn-sync"]/span'), "최신정보")
            )
        	except TimeoutException:
            	print("갱신 대기 시간이 초과 되었습니다.")
                continue
        	except UnexpectedAlertPresentException:
            	print("Alert")
            	try:
            		Alert(driver).accept()
        		except NoAlertPresentException:  # alert 닫기 오류 핸들링
            		print("alert 창 닫기 오류가 발생했습니다.")
                continue
        	except AttributeError as e:
            	print("웹 페이지 로딩이 완료되지 않았거나 일시적인 오류입니다.")
                continue
        	except WebDriverException:
            	print("갱신 중 오류가 발생했습니다.")
                continue
            time.sleep(3)
            character_list.remove(character) # 성공한 캐릭터는 갱신 리스트에서 제거
    ...

2) 결과 로그 세밀화

지금까지의 코드에서는 예외 발생 시 간단한 메시지 출력과 전체 성공 여부만 출력했는데 좀 더 세밀하게 오류 발생 위치, 갱신 실패 및 재시도 횟수, 작동 시간 등을 출력 하도록 코드를 바꾸어봤습니다.

...
import datetime


def main():
	success_cnt = 0  # 최종 갱신 성공 계정 수
    fail_cnt = 0  # 실패 횟수
    retry_cnt = 0 # 재시도 횟수
    total_cnt = 0 # 총 계정 개수
    start = datetime.datetime.now()
    
    ...
    
    
	while len(character_list) > 0:  # 갱신 캐릭터 리스트가 모두 없어질 때까지 반복(성공 시 리스트에서 캐릭터 삭제)
		for character in character_list:
        	# 각각 except 절에 fail_cnt += 1 코드 추가
            
            ...
            
            character_list.remove(character) # 성공한 캐릭터는 갱신 리스트에서 제거
            success_cnt += 1  # 최종 성공 했으므로 성공 카운트 증가
       
	finish = datetime.datetime.now()
    print(f"=== 경과 시간: {finish - start} ===\n")
    print(f"=== 총 캐릭터: {total_cnt}, 성공: {success_cnt}, 재시도: {retry_cnt}, 실패: {fail_cnt} ===\n")

3. 로그 이메일 필터링

성공한 결과 로그는 굳이 보지 않아도 되기 때문에 받은 편지함이 아닌 다른 편지함으로 필터링 할 필요가 있습니다.
성공 시에 특수한 문자열을 출력하도록 하고 해당 문자열이 포함된 성공 로그를 gmail 필터링 기능을 통해 성공/실패 로그 수신함을 분리해 보았습니다.

1) 성공 시 특수 문자열 출력

main 맨 아래에 코드를추가 했습니다.

if total_cnt == success_cnt:
	print("Maple 캐릭터 정보 갱신 성공!")

2) gmail 필터링 적용

gmail 오른쪽 위의 톱니바퀴를 누르고 모든 설정 보기를 클릭합니다.

필터 및 차단된 주소 탭을 클릭해 새 필터 만들기를 클릭합니다. 포함하는 단어에 성공 시 출력하는 특수 문자열을 입력하고 필터 만들기를 클릭합니다.

다음으로 나오는 설정에서 받은편지함 건너 뛰기를 체크하고 라벨 선택에서 새 라벨을 만들어 로그를 받을 라벨을 생성해줍니다.

그러면 왼쪽 아래에 라벨이 생기게 되고 성공 로그는 받은 편지함에 바로 표시 되지 않고 해당 라벨로 들어가 불필요한 로그가 받은 메일함에 뜨는 상황을 막을 수 있습니다.

4. 다음 고민

다른 게임 캐릭터 정보도 지금 코드를 바탕으로 갱신할 수 있지 않을까?🤔

5. Reference

지메일(gmail) 필터에 라벨 붙여 분류하는 방법

profile
여러가지를 시도하는 학생입니다

0개의 댓글