✅ Python
✅ Python Debuger
✅ Pylance
나는 최신 확장 버전이 아닌 특정 릴리즈가 필요했기 때문에 VSIX 파일을 찾았지만 제공이 되지 않아 VSCode 버전에 맞는 확장을 자동 설치했음
VSCode 확장 경로
C:\Users\로컬-PC\.vscode\extensionsextension.json
{"version":"2024.3.2","location":{"$mid":1,"path":"/c:/Users/로컬-PC/.vscode/extensions/ms-python.vscode-pylance-2024.3.2","scheme":"file"},"relativeLocation":"ms-python.vscode-pylance-2024.3.2","metadata":{"id":"","publisherId":"","publisherDisplayName":"Microsoft","targetPlatform":"undefined","isApplicationScoped":false,"updated":true,"isPreReleaseVersion":false,"installedTimestamp":1713315311220,"preRelease":false}}
이런 JSON 형태로 원하는 확장들이 정의 되어있는데
이 중 필요한 3개의 Python 확장 정보를 추출
extension.json에 붙여넣기 후 extension 폴더에 풀려져있는 확장을 그대로 옮겨오면 자동으로 로드가 된다온라인망 환경이라면 pip instal selenium 하나로 다 해결이 되었겠지만
폐쇄망에서는 타자를 치지 않는 자 패키지를 얻지 못하나니..
폐쇄망에서 패키지를 설치 가능한 whl 파일을 이용한 설치를 진행해야한다
pip download -d ./대상폴더
d 대상 폴더 지정다운로드 시 .whl 형태로 의존성을 포함한 파일들이 다운로드 되는데, 해당 파일을 압축하여 폐쇄망으로 옮기면 파일 이동은 끝난다
pip install --no-index -f *.whl ./
--no-index 옵션을 포함하여 pip install을 하면 종료 --no-index whl 순서가 아닌 의존성대로 설치-f 대상 폴더 지정이렇게 폐쇄망에서 파이썬 환경 구성 및 셀레니움 설치 시 실제로 셀레니움을 통한 테스트가 가능해진다
셀레니움 테스트 코드 구성 기준
headless 모드 사용 시 불필요한 옵션을 비활성화보안 옵션 비활성화
options.add_argument("--ignore-certificate-errors") # 인증서 무시
options.add_argument("--ignore-ssl-errors") # SSL 무시
options.add_argument("--ignore-certificate-errors-spki-list") # 인증서 공개키 에러 무시
options.add_argument("--allow-insecure-localhost") # 로컬 SSL 오류 허용
options.add_argument("--disable-web-security") # 웹 보안 비활성화
options.add_argument("--disable-blink-features=AutomationControlled") # 봇 감지 비활성화
options.add_argument("--disable-features=SSLHandshake") # SSL 핸드쉐이크 비활성화
GPU 옵션 비활성화
options.add_argument("--disable-gpu") # GPU 가속 비활성화
options.add_argument("--disable-software-rasterizer") # 소프트웨어 렌더링 비활성화
options.add_argument("--disable-features=VizDisplayCompositor") # DirectComposition 비활성화
options.add_argument("--disable-gpu-driver-bug-workarounds") # GPU 드라이버 에러 비활성화
options.add_argument("--disable-skia-runtime") # Skia 렌더링 비활성화
하드웨어 옵션 비활성화
options.add_argument("--disable-usb") # USB 검색 비활성화
options.add_argument("--disable-webusb") # 웹 USB 비활성화
options.add_argument("--disable-bluetooth") # 블루투스 비활성화
options.add_argument("--disable-default-apps")
options.add_argument("--no-default-browser-check") # 기본 브라우저 점검 비활성화
가상화 옵션 비활성화
options.add_argument("--disable-dev-shm-usage") # 공유 메모리 비활성화
options.add_argument("--no-sandbox") # 샌드박스 격리 비활성화
options.add_argument("--disable-extensions") # 확장 비활성화
기타 옵션 및 로그 레벨 조정
options.add_argument("--disable-popup-blocking") # 팝업 차단 비활성화
# options.add_argument("--user-agent=CustomUserAgent") # 사용자 지정 에이전트 설정
options.add_argument("--lang=ko") # 언어 설정
options.add_argument("--log-level=3") # info 미만 로깅 비활성화
상기와 같은 형태로 단순히 카드를 선택, 호출하기까지의 속도 테스트 코드를 작성해보았음
결제 테스트 자동화까지의 영역은 브라우저 컨트롤 영역을 벗어나기 때문에 별도 글로 분리 작성 예정
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Pool
TEST_URL = "https://test.co.kr/test/test.jsp"
USER_POOL = 100 # 동시 실행 스레드
def get_chrome_options(headless=True):
options = webdriver.ChromeOptions()
# 웹 보안 및 인증서 관련 설정
options.add_argument("--ignore-certificate-errors") # 인증서 무시
options.add_argument("--ignore-ssl-errors") # SSL 무시
options.add_argument("--ignore-certificate-errors-spki-list") # 인증서 공개키 에러 무시
options.add_argument("--allow-insecure-localhost") # 로컬 SSL 오류 허용
options.add_argument("--disable-web-security") # 웹 보안 비활성화
options.add_argument("--disable-blink-features=AutomationControlled") # 봇 감지 비활성화
options.add_argument("--disable-features=SSLHandshake") # SSL 핸드쉐이크 비활성화
# GPU 및 렌더링 관련 설정
options.add_argument("--disable-gpu") # GPU 가속 비활성화
options.add_argument("--disable-software-rasterizer") # 소프트웨어 렌더링 비활성화
options.add_argument("--disable-features=VizDisplayCompositor") # DirectComposition 비활성화
options.add_argument("--disable-gpu-driver-bug-workarounds") # GPU 드라이버 에러 비활성화
options.add_argument("--disable-skia-runtime") # Skia 렌더링 비활성화
# 하드웨어 및 장치 감지 비활성화
options.add_argument("--disable-usb") # USB 검색 비활성화
options.add_argument("--disable-webusb") # 웹 USB 비활성화
options.add_argument("--disable-bluetooth") # 블루투스 비활성화
options.add_argument("--disable-default-apps")
options.add_argument("--no-default-browser-check") # 기본 브라우저 점검 비활성화
# 가상화 환경 최적화
options.add_argument("--disable-dev-shm-usage") # 공유 메모리 비활성화
options.add_argument("--no-sandbox") # 샌드박스 격리 비활성화
options.add_argument("--disable-extensions") # 확장 비활성화
# 기타 옵션
options.add_argument("--disable-popup-blocking") # 팝업 차단 비활성화
# options.add_argument("--user-agent=CustomUserAgent") # 사용자 에이전트 설정
options.add_argument("--lang=ko") # 언어 설정
# 로그 레벨 조정
options.add_argument("--log-level=3")
# Headless 모드 설정
if headless:
options.add_argument("--headless")
options.add_argument("--disable-features=TranslateUI")
return options
def open_browser(uid):
print(f"uid[{uid}] call browser")
try:
# Chrome 드라이버 호출
options = get_chrome_options(headless=True)
# options = get_chrome_options(headless=False)
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
driver.get(TEST_URL)
print(f"uid[{uid}] open_browser: {driver.title}, 핸들: {driver.current_window_handle}")
time.sleep(3)
# 테스트 페이지 iframe 전환
iframe_el = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe_el)
print(f"uid[{uid}] switch_to.frame: {driver.title}, 핸들: {driver.current_window_handle}")
# 결제 옵션 설정
select_el = Select(driver.find_element(By.XPATH, "/html/body/form/table[4]/tbody[5]/tr[12]/td[2]/select"))
select_el.select_by_value("Y")
amt_input_el = driver.find_element(By.XPATH, "/html/body/form/table[3]/tbody/tr[6]/td[2]/input")
amt_input_el.clear()
amt_input_el.send_keys("50000")
# 결제하기 클릭
driver.find_element(By.XPATH, "/html/body/form/p[1]/table/tbody/tr/td/input").click()
# 팝업 결제창으로 윈도우 전환
main_window = driver.current_window_handle # 현재 창 저장
WebDriverWait(driver, 5).until(lambda d: len(d.window_handles) > 1) # 새 창 열림 대기
page_popup_window = driver.window_handles[-1] # 가장 마지막 창
driver.switch_to.window(page_popup_window)
print(f"uid[{uid}] page_popup_window: {driver.title}, 핸들: {driver.current_window_handle}")
# JS 렌더링 측정
load_time = driver.execute_script(
"return window.performance.timing.loadEventEnd - window.performance.timing.navigationStart"
)
print(f"uid[{uid}] JS 렌더링 소요 시간: {load_time / 1000:.3f} 초")
# 렌더링 속도 측정
start_time = time.time()
WebDriverWait(driver, 5).until(
EC.visibility_of_element_located((By.XPATH, "/html/body/div[2]/div[2]/div[2]/div[1]"))
)
end_time = time.time()
find_el_time = end_time - start_time
print(f"uid[{uid}] 페이지 렌더링 소요 시간: {find_el_time:.3f} 초")
# 약관 동의 및 다음 버튼 클릭
driver.find_element(By.XPATH, "//*[@id='card_agreement_all']").click()
driver.find_element(By.XPATH, "//*[@id='card_payment_step1']/ul/li[9]/span[2]/button").click()
# 카드 선택
pick_card(driver, "KB", uid)
# 다음 버튼 클릭
driver.find_element(By.XPATH, "//*[@id='card_payment_step3']/ul/li[13]/span[2]/button").click()
# ACS 팝업 전환
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1) # 새 창 열림 대기
acs_popup_window = driver.window_handles[-1] # ACS 창
driver.switch_to.window(acs_popup_window)
print(f"uid[{uid}] acs_popup_window: {driver.title}, 핸들: {driver.current_window_handle}")
# iframe 전환
acs_frame_el = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(acs_frame_el)
print(f"uid[{uid}] acs_frame_el: {driver.title}, 핸들: {driver.current_window_handle}")
# ACS 창 내 ISP 버튼 클릭
WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.XPATH, "//*[@id='tabView01']/div/ul/li[4]/button"))
)
driver.find_element(By.XPATH, "//*[@id='tabView01']/div/ul/li[4]/button").click()
# focused_el = driver.execute_script("return document.activeElement;")
# print(f"isp_btn_el: {isp_btn_el} focused_el: {focused_el}")
print(f"uid[{uid}] isp_btn_el: {driver.title}, 핸들: {driver.current_window_handle}")
# # 아이프레임 이전 복귀
# driver.switch_to.default_content()
# print(f"switch_to.default_content: {driver.title}, 핸들: {driver.current_window_handle}")
# # 팝업 닫기
# driver.close()
except Exception as e:
print(f"uid[{uid}] Exception {e}")
finally:
driver.switch_to.window(main_window)
print(f"switch_to.default_content: {driver.title}, 핸들: {driver.current_window_handle}")
driver.switch_to.default_content()
driver.quit()
print(f"uid[{uid}] exit browser")
def pick_card(driver, card_id, uid):
driver.find_element(By.CSS_SELECTOR, f"input[type='radio'][value='{card_id}']").click()
print(f"uid[{uid}] 카드 {card_id} 선택")
if __name__ == "__main__":
with ThreadPoolExecutor(max_workers=USER_POOL) as executor:
executor.map(open_browser, range(1, USER_POOL + 1))