Post

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

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

HTS login automation workflow

이번 글에서는 삼성증권 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.