Post

삼성증권 POP HTS 실행과 로그인 자동화 코드 정리

삼성증권 POP HTS 실행과 로그인 자동화 코드 정리

이번 글에서는 삼성증권 POP HTS를 자동 실행하고, 인증서 비밀번호를 입력한 뒤, 창 크기와 위치를 고정하는 코드 흐름을 정리해본다.

이 코드는 단순히 프로그램을 실행하는 수준이 아니라, 실제 자동매매를 안정적으로 돌리기 위해 필요한 다음 과정을 포함한다.

  • POP HTS가 이미 실행 중인지 확인
  • 실행 중이면 기존 창 활성화
  • 실행되어 있지 않으면 프로그램 시작
  • 인증서 비밀번호 자동 입력
  • 로그인 이후 확인 팝업 처리
  • 창 크기와 위치를 일정하게 맞춤

자동화 관점에서 보면 이 단계는 주문 로직보다도 더 중요할 수 있다. 로그인 단계가 흔들리면 이후의 UI 탐색, 버튼 클릭, 이미지 인식이 전부 불안정해지기 때문이다.


전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import pygetwindow

def is_pop_running():
    for w in pygetwindow.getAllWindows():
        if w.title and ("POP" in w.title or "삼성" in w.title):
            w.activate()
            return True
    return False

def enter_cert_password(run_dir: Path):
    # 인증서 비번 입력칸 클릭(라벨+오프셋 추천)
    screen.wait_exists("cert_pw_label.png", timeout=10, confidence=0.8)
    screen.click_field_by_label(run_dir, "cert_pw_label.png", x_offset=160, conf=0.80)
    screen.ctrl_a()
    time.sleep(0.05)
    CERT_PW = os.getenv("CERT_PW")
    screen.safe_type_symbol(CERT_PW)  # 클립보드 붙여넣기
    time.sleep(0.1)
    screen.wait_and_click("cert_btn_confirm.png", run_dir, timeout=10, confidence=0.80)
    time.sleep(5)
    if screen.exists_on_screen("btn_confirm_after_login.png", confidence=0.80):
        screen.wait_and_click("btn_confirm_after_login.png", run_dir,
                                       timeout=10, confidence=0.80)
    time.sleep(5)

def start_pop_hts(run_dir: Path):
    if is_pop_running():
        t_util.log("POP HTS 이미 실행 중")
        normalize_pop_window()
        return

    for i in range(2):
        try:
            t_util.log("POP HTS 실행 시작")
            subprocess.Popen(POP_EXE_PATH)
            time.sleep(15)  # 실행 대기
            enter_cert_password(run_dir)
            normalize_pop_window()
            time.sleep(10)  # 실행 대기
            screen.close_login_notice(run_dir)
            time.sleep(10)  # 실행 대기
            break
        except Exception as ex:
            t_util.log(ex)

def normalize_pop_window(target_width=1900, target_height=1100):
    # POP HTS 창을 지정한 크기로 변경 + 화면 중앙 배치
    windows = pygetwindow.getAllWindows()
    pop_win = None

    for w in windows:
        if w.title and ("POP" in w.title or "삼성" in w.title):
            pop_win = w
            break

    if not pop_win:
        raise RuntimeError("POP HTS 창을 찾지 못했습니다.")

    if pop_win.isMinimized:
        pop_win.restore()

    pop_win.activate()
    time.sleep(0.2)

    # 현재 화면 해상도
    screen_width, screen_height = pyautogui.size()

    # 중앙 좌표 계산
    left = (screen_width - target_width) // 2
    top = (screen_height - target_height) // 2

    # 크기 및 위치 설정
    pop_win.resizeTo(target_width, target_height)
    time.sleep(0.1)
    pop_win.moveTo(left, top)
    time.sleep(0.3)

    t_util.log(f"POP HTS 창 크기 고정: {target_width}x{target_height}")

1. is_pop_running(): POP HTS가 이미 떠 있는지 확인

가장 먼저 하는 일은 삼성증권 POP HTS가 이미 실행 중인지 확인하는 것이다.

1
2
3
4
5
6
def is_pop_running():
    for w in pygetwindow.getAllWindows():
        if w.title and ("POP" in w.title or "삼성" in w.title):
            w.activate()
            return True
    return False

이 함수는 pygetwindowgetAllWindows()로 현재 열려 있는 모든 창을 순회하면서, 제목에 "POP" 또는 "삼성"이 포함된 창을 찾는다.

찾으면 바로:

  • 해당 창을 활성화하고
  • True를 반환한다.

찾지 못하면 False를 반환한다.

이 방식의 장점

  • 구현이 단순하다.
  • 삼성증권 창이 이미 떠 있는지 빠르게 확인할 수 있다.
  • 실행 중인 창을 바로 앞으로 가져올 수 있다.

주의할 점

창 제목 문자열에 의존하므로, 실제 환경에서 창 제목이 바뀌거나 예기치 않은 다른 삼성 관련 창이 떠 있을 경우 오탐 가능성은 있다. 그래도 HTS 실행 여부를 거칠게 확인하는 용도로는 꽤 실용적이다.


2. enter_cert_password(): 인증서 비밀번호 입력 자동화

로그인 단계에서 가장 중요한 부분은 인증서 비밀번호 입력이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def enter_cert_password(run_dir: Path):
    # 인증서 비번 입력칸 클릭(라벨+오프셋 추천)
    screen.wait_exists("cert_pw_label.png", timeout=10, confidence=0.8)
    screen.click_field_by_label(run_dir, "cert_pw_label.png", x_offset=160, conf=0.80)
    screen.ctrl_a()
    time.sleep(0.05)
    CERT_PW = os.getenv("CERT_PW")
    screen.safe_type_symbol(CERT_PW)  # 클립보드 붙여넣기
    time.sleep(0.1)
    screen.wait_and_click("cert_btn_confirm.png", run_dir, timeout=10, confidence=0.80)
    time.sleep(5)
    if screen.exists_on_screen("btn_confirm_after_login.png", confidence=0.80):
        screen.wait_and_click("btn_confirm_after_login.png", run_dir,
                                       timeout=10, confidence=0.80)
    time.sleep(5)

이 함수의 핵심은 입력칸을 직접 찾는 대신, 라벨 이미지를 찾고 그 기준으로 오프셋을 적용해 입력칸을 클릭하는 방식이다.

왜 라벨 + 오프셋 방식을 쓰는가

HTS는 입력창 자체를 정확히 이미지 매칭하기 어려운 경우가 있다. 반면 "인증서 비밀번호" 같은 라벨은 비교적 안정적으로 잡히는 경우가 많다.

그래서 다음처럼 처리한다.

  1. cert_pw_label.png 이미지가 화면에 나타날 때까지 대기
  2. 해당 라벨 기준으로 오른쪽 x_offset=160 위치 클릭
  3. Ctrl+A로 기존 텍스트 전체 선택
  4. 환경변수에서 인증서 비밀번호 읽기
  5. 안전한 입력 방식으로 비밀번호 입력
  6. 확인 버튼 클릭
  7. 로그인 이후 추가 확인 팝업이 있으면 한 번 더 처리

screen.safe_type_symbol()를 쓴 이유

보통 pyautogui.write() 같은 직접 타이핑 방식은 한글, 특수문자, 보안 입력 환경에서 불안정할 수 있다. 그래서 클립보드 기반 붙여넣기 방식을 감싼 safe_type_symbol() 같은 함수를 쓰면 안정성이 올라간다.

환경변수 사용의 의미

1
CERT_PW = os.getenv("CERT_PW")

비밀번호를 코드에 하드코딩하지 않고 환경변수에서 읽어오면:

  • 코드 저장소 유출 위험 감소
  • 설정 분리 가능
  • 운영 환경별 비밀번호 관리 용이

라는 장점이 있다.


3. start_pop_hts(): 전체 실행 흐름 제어

실제로 프로그램을 시작하는 메인 함수는 아래 코드다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def start_pop_hts(run_dir: Path):
    if is_pop_running():
        t_util.log("POP HTS 이미 실행 중")
        normalize_pop_window()
        return

    for i in range(2):
        try:
            t_util.log("POP HTS 실행 시작")
            subprocess.Popen(POP_EXE_PATH)
            time.sleep(15)  # 실행 대기
            enter_cert_password(run_dir)
            normalize_pop_window()
            time.sleep(10)  # 실행 대기
            screen.close_login_notice(run_dir)
            time.sleep(10)  # 실행 대기
            break
        except Exception as ex:
            t_util.log(ex)

이 함수는 크게 두 갈래로 나뉜다.

이미 실행 중인 경우

1
2
3
4
if is_pop_running():
    t_util.log("POP HTS 이미 실행 중")
    normalize_pop_window()
    return

이미 POP HTS가 켜져 있으면 굳이 다시 실행하지 않고:

  • 로그 남기고
  • 창 크기와 위치만 정리한 뒤
  • 종료한다.

이렇게 하면 중복 실행을 피할 수 있다.

실행되어 있지 않은 경우

실행 중이 아니라면 최대 2번까지 시도한다.

1
for i in range(2):

한 번 실패하더라도 바로 포기하지 않고 재시도할 수 있게 한 것이다.

실행 순서는 다음과 같다.

  1. HTS 실행 시작 로그 남김
  2. subprocess.Popen(POP_EXE_PATH)로 프로그램 실행
  3. 15초 대기
  4. 인증서 비밀번호 입력
  5. 창 크기/위치 정리
  6. 로그인 후 공지 팝업 닫기
  7. 마무리 대기 후 종료

sleep()이 많은가

이런 UI 자동화 코드는 화면이 바뀌기 전에 다음 동작을 실행하면 실패하기 쉽다. 특히 HTS 같은 무거운 데스크톱 앱은:

  • 실행 직후 로딩 시간
  • 로그인 후 초기화 시간
  • 팝업 생성 시간

이 일정하지 않을 수 있다.

그래서 sleep()은 다소 투박하지만, 실제 현장에서는 매우 자주 쓰는 안정화 수단이다.

다만 장기적으로는 단순 대기보다:

  • 특정 화면 요소가 나타날 때까지 기다리기
  • 창 제목 변경 확인
  • 버튼 활성화 여부 확인

같은 조건 기반 대기로 바꾸는 것이 더 안정적이다.


4. normalize_pop_window(): 창 크기와 위치를 고정

자동화에서는 창 크기와 위치를 일정하게 맞추는 것이 매우 중요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def normalize_pop_window(target_width=1900, target_height=1100):
    # POP HTS 창을 지정한 크기로 변경 + 화면 중앙 배치
    windows = pygetwindow.getAllWindows()
    pop_win = None

    for w in windows:
        if w.title and ("POP" in w.title or "삼성" in w.title):
            pop_win = w
            break

    if not pop_win:
        raise RuntimeError("POP HTS 창을 찾지 못했습니다.")

    if pop_win.isMinimized:
        pop_win.restore()

    pop_win.activate()
    time.sleep(0.2)

    # 현재 화면 해상도
    screen_width, screen_height = pyautogui.size()

    # 중앙 좌표 계산
    left = (screen_width - target_width) // 2
    top = (screen_height - target_height) // 2

    # 크기 및 위치 설정
    pop_win.resizeTo(target_width, target_height)
    time.sleep(0.1)
    pop_win.moveTo(left, top)
    time.sleep(0.3)

    t_util.log(f"POP HTS 창 크기 고정: {target_width}x{target_height}")

이 함수는 POP HTS 창을 찾아서:

  • 최소화되어 있으면 복원
  • 활성화
  • 지정된 크기로 리사이즈
  • 화면 중앙으로 이동

왜 이게 중요한가

이미지 매칭이나 좌표 클릭은 창 위치와 크기에 영향을 많이 받는다. 창이 조금만 달라져도:

  • 버튼 좌표가 어긋나고
  • 이미지 인식 결과가 흔들리고
  • 다음 단계 자동화가 실패

그래서 자동화 시작 단계에서 창을 일관된 상태로 맞추는 것은 거의 필수에 가깝다.

중앙 배치 계산

1
2
left = (screen_width - target_width) // 2
top = (screen_height - target_height) // 2

현재 모니터 해상도 기준으로 창을 가운데에 놓기 위한 좌표 계산이다.

이렇게 하면 매번 같은 위치에 HTS 창을 배치할 수 있다.


5. 이 코드의 전체 흐름 요약

이 코드는 아래 순서로 동작한다.

1
2
3
4
5
6
POP HTS 실행 여부 확인
→ 이미 실행 중이면 활성화 및 창 정렬
→ 실행 중이 아니면 HTS 시작
→ 인증서 비밀번호 입력
→ 로그인 후 팝업 처리
→ 창 크기와 위치 고정

자동매매 관점에서 보면 이 단계는 주문보다 먼저 보장되어야 하는 사전 준비 루틴이다.


6. 이 코드의 장점

1) 중복 실행 방지

이미 POP HTS가 떠 있으면 다시 실행하지 않는다.

2) 로그인 자동화 포함

인증서 비밀번호 입력과 확인 버튼 클릭까지 처리한다.

3) 팝업 대응 포함

로그인 후 추가 확인 버튼이나 공지창도 이어서 처리할 수 있다.

4) 창 상태 정규화

이미지 매칭과 좌표 클릭이 흔들리지 않도록 창 크기와 위치를 일정하게 맞춘다.


7. 개선 포인트

실전에서 더 안정적으로 만들려면 아래와 같은 보완도 고려할 수 있다.

1) 제목 문자열만으로 창 식별하지 않기

현재는 "POP" 또는 "삼성" 문자열에 의존한다. 가능하면 더 구체적인 창 제목이나 프로세스 기준 식별이 있으면 더 좋다.

2) sleep() 대신 조건 기반 대기 사용

예를 들면:

  • 인증서 입력창 등장까지 대기
  • 로그인 완료 후 메인 화면 특정 요소 확인
  • 공지창 존재 여부 체크

처럼 바꾸면 속도와 안정성이 둘 다 좋아진다.

3) 예외 로그를 더 자세히 남기기

현재는 예외 객체만 기록한다.

1
2
except Exception as ex:
    t_util.log(ex)

여기에:

  • 현재 단계
  • 스크린샷
  • 마지막 탐색 대상 이미지
  • 재시도 횟수

까지 남기면 디버깅이 쉬워진다.

4) 재시도 실패 시 명시적으로 실패 처리

지금은 2회 시도 후에도 실패하면 조용히 함수가 끝날 수 있다. 실전 자동매매라면 마지막에는 raise 하거나 상위 로직으로 실패를 명확히 전달하는 편이 더 안전하다.


8. 마무리

삼성증권 POP HTS 자동화에서 실행과 로그인 단계는 단순 준비 작업이 아니다. 이 단계가 안정적으로 동작해야 이후의 주문 화면 탐색, 버튼 클릭, 체결 처리까지 이어질 수 있다.

특히 이 코드에서 핵심은 다음 세 가지다.

  • 실행 여부를 먼저 확인해 중복 실행을 막는다.
  • 인증서 비밀번호 입력을 이미지 기반으로 처리한다.
  • 창 크기와 위치를 고정해 이후 자동화를 안정화한다.

자동매매는 주문 로직만 잘 짜는 것으로 끝나지 않는다. 실제로는 프로그램 실행 → 로그인 → 팝업 정리 → 창 상태 고정 같은 사전 루틴이 전체 안정성을 좌우한다.


This post is licensed under CC BY 4.0 by the author.

© standspring. Some rights reserved.

Using the Chirpy theme for Jekyll.