본문 바로가기

영웅문Global 핸들링

영웅문Global 로그인

728x90

1차에서 수행했던 내용들을 토대로 영웅문 Global을 핸들링하는 것이 가능하다는 것을 알게 되었습니다.

2023.11.30 - [영웅문 Global 핸들링] - 영웅문 Global 핸들링을 위한 환경 설치 (feat. 파이썬)

실행해 놓은 영웅문Global영웅문 Global 프로그램을 연결해서 사용했기에 프로그램 실행 전 영웅문 Global을 실행해야 하는 문제가 있었습니다. 매매 방법론을 로직으로 구현하고 완전 자동으로 영웅문 Global을 핸들링하려면 가장 먼저 해결해야 하는 것이 로그인입니다.

키움증권의 영웅문Global 로그인 창 +_+

먼저 어떤 방식으로 개발할지 생각해 보겠습니다. 흐름은 영웅문 Global 실행 ⇒ 로그인창 확인 ⇒ 로그인 정보 입력 ⇒ 영웅문 Global 실행 순서입니다. 각 단계에 도달했는지 체크하기 위한 함수가 필요해 보입니다. 아무 상태가 아니라면 영웅문 Global 실행을 해야 하고, 로그인창 상태라면 로그인 정보 입력을 해야 하며, 영웅문 Global 실행 상태라면 로그인 프로세스는 성공적으로 마무리하게 됩니다. 그러면 바로 소스 및 내용을 확인해 볼까요?

 

영웅문 Global 실행

기본적인 경로에 영웅문 Global을 설치했다면 C: 에 설치가 되었을 것 입니다. 해당 경로에 있는 nfrunlite.exe 파일을 실행하면 영웅문 Global 로그인 창이 뜹니다. 실행 파일을 직접 실행하므로 경로를 잘 체크해 주세요. 저의 경로는 C:\KiwoomGlobal\bin\nfrunlite.exe입니다.

또는 윈도 검색창에서 영웅문 Global을 검색한 후 파일 위치 열기를 클릭합니다.

윈도우 검색 기능

윈도10에서는 위와 같이 생겼습니다. 단축키는 (윈도우키 + s)입니다.

윈도우 검색을 통한 영웅문Global 위치 찾기

파일 위치 열기를 클릭하면 윈도우 탐색기가 뜨는데 여기에 바로가기 파일이 있습니다.

윈도우 탐색기에 해당 파일의 위치가 뜹니다.

이 파일을 마우스 우클릭해서 속성을 누르면 해당 실행 파일의 경로가 있습니다.

바로 가기 파일의 속성을 보면 대상의 정확한 경로가 나와있습니다.

어렵게 찾았지만 대부분 그냥 설치했다면 저 경로에 있을 것이므로 우선 찾아보고 없다면 이 방법을 사용해 보시기를 권합니다.

어쨌든 지금까지 실행 파일을 찾은 이유는 이 파일을 파이썬으로 실행하기 위해서였습니다. 아래의 간단한 명령어로 영웅문 Global 로그인 창을 띄울 수 있습니다.

app = Application().start('C:/KiwoomGlobal/bin/nfrunlite.exe')
time.sleep(3)

컴퓨터의 성능에 따라서 3초 이상 기다려야 할 수 있습니다. +_+

 

영웅문Global 로그인 창

로그인 창이 떴나요?! 로그인 창에서 핸들링할 영역은 다음과 같습니다. 심플하게 ID 비밀번호와 인증비밀번호 입력하는 것으로 해보겠습니다.

로그인 창에서 핸들링 할 수 있는 부분들은 많지만 오늘은 2영역만 핸들링 해보겠습니다.

로그인 창을 핸들링 할 수 있도록 ID 비밀번호와 인증비밀번호 부분을 가져오겠습니다.

login_window = app.window(title='영웅문Global Login')
id_field = None
pw_field = None
for i, child in enumerate(login_window.children()):
    if child.window_text().startswith("영문+숫자 5"):
        id_field = login_window.children()[i - 1]
    elif child.window_text().startswith("영문+숫자+특수"):
        pw_field = login_window.children()[i - 1]

    if id_field and pw_field:
        break

로그인창에 적혀있는 텍스트로 내가 핸들링할 입력창을 찾습니다. 그리고 찾은 텍스트 앞의 Edit에 입력을 할 수 있기 때문에 i-1 값으로 가져옵니다.

가져왔다면 ID 비밀번호와 인증비밀번호를 넣어서 로그인을 합니다.

if id_field and pw_field:
    id_field.type_keys(user_id)
    pw_field.type_keys(user_pw).type_keys('{ENTER}')
    time.sleep(5)

user_id, user_pw는 여러분들 실제 내용으로 변경해 주세요. 앞서서도 잠깐 말씀드렸지만 고객 ID는 이미 저장되어 있다고 가정했습니다. 여러 고객 ID로 로그인하면서 처리하고 싶으신 분들은 위의 소스를 참고해서 고객 ID를 핸들링할 수 있도록 해보는 것도 좋은 실습이 될 것 같습니다.

모두 입력 후 로그인을 시도하고 5초를 쉬었는데 그다음은 영웅문 Global 프로그램이 뜨는 것을 확인해야 합니다. (업데이트나 기타 다른 이유로 영웅문 Global 프로그램이 안뜰 수도 있습니다.)

for i in range(10):
    app_main, msg = check_hero_global_is_running()
    if msg.startswith("영웅문Global"):
        break
    time.sleep(5)

check_hero_global_is_running() 함수를 통해 체크하고 있는데 이 함수에 대해서는 바로 다음에 설명드리겠습니다. 이 함수를 통해서 영웅문 Global이 실행된 상태를 확인하게 됩니다.

만약 예상치 못하게 실행이 지연되는 등의 문제가 발생할 수 있습니다. 예외적인 처리를 잘해야 원하는 동작을 수행시킬 수 있습니다. 그러나 예외 처리는 안 되는 경험을 하고 보완하는 시간을 보내야 합니다. 그 시간이 지금은 아닐 겁니다. 지금은 단순하게 동작하는 것을 목표로 하는 것이 좋겠습니다. 나중에 예외 처리 부분에 대한 얘기를 다시 해 볼 수 있도록 준비하겠습니다.

def check_hero_global_is_running():
    process_id = find_process_id("nfrunlite.exe")
    process_login_id = find_process_id("nfstarter.exe")

    msg = ""
    if process_id == 0:
        if process_login_id == 0:
            app = Application().start('C:/KiwoomGlobal/bin/nfrunlite.exe')
            time.sleep(3)
            msg = "로그인창 실행"
        else:
            app = Application().connect(process=process_login_id)
            msg = "로그인창 연결"
    else:
        app = Application().connect(process=process_id)
        msg = "영웅문Global 연결"

    return app, msg

이 함수는 또 다른 find_process_id 함수를 호출합니다. +_+

저도 처음에 프로그래밍을 배웠을 때 이렇게 호출하고 호출하고, 타고 타고 들어가는 과정이 너무나 힘들었습니다. 그런데 그렇게 저도 하고 있네요. ㅠ_ㅠ check_hero_global_is_running에 대해 간단히 설명드리고 find_process_id를 살펴보겠습니다.

영웅문 Global이 실행되고 있으면 process_id가 있는 것이고, 영웅문 Global Login 창이 실행되고 있으면 process_login_id가 있게 됩니다. 이를 통해 현재가 어떤 상태인지를 확인하는 로직 입니다. 둘 다 0이라면 아무것도 실행된 상태가 아닌 것으로 보고 로그인창을 실행합니다. process_id는 0인데 process_login_id는 0이 아니라면 영웅문Global Login 창이 실행되고 있다는 의미고 로그인 창을 연결합니다. 마지막으로 process_id가 0이 아니면 영웅문 Global이 실행되고 있다는 의미이므로 이번 로그인을 위한 과정에서는 최종 단계에 도달한 것이라 할 수 있겠습니다.

보통은 함수가 check로 시작하므로 어떤 상태인지 체크만 하고 체크 결과만 넘겨주게 만들어야 하는데 그런 작업은 미래의 저에게 미뤘습니다. (msg도 보통은 상태값으로 정의하고 사용합니다.)

이어서 find_process_id 함수를 살펴보겠습니다.

def find_process_id(process_name):
    process_id = 0
    for process in psutil.process_iter(['pid', 'name']):
        if process.info['name'] == process_name:
            process_id = process.info['pid']
            break
    return process_id

내 컴퓨터에서 수행되고 있는 프로세스를 찾는 로직입니다. process_name을 알아야 하는데 이 부분이 조금 어려울 수 있지만 아래 내용을 참고한다면 금방 이해되실 겁니다.

 

실행 중인 프로그램의 process_name을 찾는 방법

작업관리자를 실행합니다. control + shift + esc 키를 누르면 열립니다. 또는 작업표시줄을 우클릭해서 실행할 수도 있습니다.

윈도우에서 제공하는 작업관리자 입니다. 보통 프로세스 죽일려고 열게 됩니다. +_+

이 작업관리자는 윈도우 버전에 따라서 다른 모양일 수 있습니다. 윈도우11으로 언젠가 가겠지만 아직은 윈도우10이라서(?) 다행입니다.

여기서 내가 실행한 프로그램을 빠르게 확인할 수 있습니다. 영웅문 G 로그인창(32비트)이 눈에 띄네요. 해당 항목을 선택하고 우클릭하면 여러 가지 옵션이 뜨는데 이 중에서 “세부 정보로 이동(G)”를 선택하겠습니다.

보통은 작업 끝내기(E) 하셨겠지만 오늘은 세부 정보로 이동(G) 해주세요.

그러면 자연스럽게 탭이 이동되면서 영웅문 G 로그인창(32비트)의 프로세스를 보여줍니다.

뭔가 프로그램 같은 내용으로 바뀐 것 같습니다. +_+

이제 앞서 작성했던 소스코드에 적혀있던 nfstarter.exe가 보이실 겁니다. 이 이름으로 저희는 실행 중인 프로세스를 찾을 수 있습니다. 그리고 찾게 되면 해당 프로세스의 ID (PID)를 얻을 수 있고 이 PID로 연결한 후 프로그램들을 핸들링하게 됩니다.

 

로그인 전체 로직

핵심 로직을 부분 부분 설명 드렸지만 어쨌든 최종적으로 동작하는 소스가 필요할 것 같습니다.

더보기
import time

import psutil
from pywinauto.application import Application

user_id = "여러분의 ID비밀번호"
user_pw = "여러분의 인증서비밀번호"


def find_process_id(process_name):
    process_id = 0
    for process in psutil.process_iter(['pid', 'name']):
        if process.info['name'] == process_name:
            process_id = process.info['pid']
            break
    return process_id


def hero_global_login(app):
    login_window = app.window(title='영웅문Global Login')
    id_field = None
    pw_field = None
    for i, child in enumerate(login_window.children()):
        if child.window_text().startswith("영문+숫자 5"):
            id_field = login_window.children()[i - 1]
        elif child.window_text().startswith("영문+숫자+특수"):
            pw_field = login_window.children()[i - 1]

        if id_field and pw_field:
            break

    if id_field and pw_field:
        id_field.type_keys(user_id)
        pw_field.type_keys(user_pw).type_keys('{ENTER}')
        time.sleep(5)

    for i in range(10):
        app_main, msg = check_hero_global_is_running()
        if msg.startswith("영웅문Global"):
            break
        time.sleep(5)

    return app_main


def find_controls_recursively(control, class_name):
    found_controls = []
    for child in control.children():
        if child.class_name() == class_name:
            found_controls.append(child)
        # 자식 컨트롤들에 대해서도 재귀적으로 탐색
        found_controls.extend(find_controls_recursively(child, class_name))
    return found_controls


def check_hero_global_is_running():
    process_id = find_process_id("nfrunlite.exe")
    process_login_id = find_process_id("nfstarter.exe")

    msg = ""
    if process_id == 0:
        if process_login_id == 0:
            app = Application().start('C:/KiwoomGlobal/bin/nfrunlite.exe')
            time.sleep(3)
            msg = "로그인창 실행"
        else:
            app = Application().connect(process=process_login_id)
            msg = "로그인창 연결"
    else:
        app = Application().connect(process=process_id)
        msg = "영웅문Global 연결"

    return app, msg


app, msg = check_hero_global_is_running()
if msg.startswith("로그인창 실행"):
    process_login_id = find_process_id("nfstarter.exe")
    app = Application().connect(process=process_login_id)
    app_main = hero_global_login(app)
elif msg.startswith("로그인창 연결"):
    app_main = hero_global_login(app)
else:
    app_main = app

main_window = app_main.window(title='영웅문Global')
main_window.maximize()
time.sleep(1)

함수로 되어있지 않은 아래 내용이 영웅문 Global을 실행하고 로그인하는 메인 로직입니다. 오늘도 관련 내용을 읽어주셔서 감사합니다. 이제 영웅문 Global을 자동으로 실행할 수 있게 되었습니다. 이와 유사하게 윈도우에 설치된 많은 응용 프로그램들을 실행하고, 실행 중인지 체크하고, 핸들링하실 수 있을 겁니다. 파이팅!!

여기까지 내용을 직접 실행해 보셨나요? 부족한 점이 있거나 어려운 점이 있다면 언제든지 댓글, 이메일로 문의주세요. 문의를 주시는데 시간을 쏟으신 만큼 최선을 다해 답변드리겠습니다.

다음은 전체적인 로직이 반복적으로 수행되는 방법에 대해 설명 드리겠습니다.

관심의 표현은 저에게 큰 힘이 됩니다. 😍 도움을 주셔서 정말 감사합니다.

반응형