견적어플알림 + 자동견적 프로그램 기획

## 1. 세무사견적어플 프로그램 기획

저는 세무사견적어플에서 활동하는 세무사/회계사입니다.

저는 어플을 통해 세무상담 견적을 받습니다.

최근 견적 어플의 경쟁심화로 고객들의 세무 상담 요청에 신속하게 대응하기 위해 파이썬을 활용한 견적 자동화 + 소리알림 프로그램을 개발하게 되었습니다. 본 프로그램은 새로운 상담 요청을 실시간으로 모니터링하고, 자동으로 견적과 상세 설명을 제공함으로써 고객 응대 시간을 최소화하는 것을 목표로 합니다.

코드는 claude 로 작성하였습니다. 

https://사이트명/requests/received?from=gnb

여기서 내 아이디로 로그인 한다음에 새로운 request 가 오면 바로 알려주는 기능을 뭘로 만들 수 있을까?

[1] Python + Selenium을 이용한 방법:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from plyer import notification  # 데스크톱 알림용

def check_new_requests(username, password):
    driver = webdriver.Chrome()
    driver.get("https://사이트주소/requests/received")
    
    # 로그인
    # (로그인 폼의 실제 element ID나 class name으로 수정 필요)
    driver.find_element(By.ID, "username").send_keys(username)
    driver.find_element(By.ID, "password").send_keys(password)
    driver.find_element(By.CLASS_NAME, "login-button").click()
    
    previous_requests = None
    
    while True:
        try:
            # 요청 목록을 가져옴 (실제 element selector로 수정 필요)
            requests = driver.find_elements(By.CLASS_NAME, "request-item")
            current_requests = len(requests)
            
            if previous_requests is not None and current_requests > previous_requests:
                # 새로운 요청이 있을 때 알림
                notification.notify(
                    title='새로운 요청!',
                    message=f'{current_requests - previous_requests}개의 새로운 요청이 있습니다.',
                    app_icon=None,
                    timeout=10,
                )
            
            previous_requests = current_requests
            time.sleep(60)  # 1분마다 체크
            driver.refresh()
            
        except Exception as e:
            print(f"에러 발생: {e}")
            driver.refresh()
            time.sleep(60)
            
    driver.quit()

이외에도

[2] Python + requests 라이브러리를 이용한 방법 (API가 있는 경우):

[3] 크롬 확장 프로그램으로 만드는 방법도 있습니다:

위의 세가지 방법을 추천하였으나 저는 셀레니움을 이용한 방법으로 진행하기로 하였습니다

왜냐면 claude 가 추천 했기 때문 ㅋㅋ

추천하는 방법은 Python + Selenium 입니다. 이유는:
로그인이 필요한 페이지를 자동화하기 쉽습니다

실제 브라우저를 통해 접근하므로 차단될 가능성이 적습니다

데스크톱 알림을 쉽게 구현할 수 있습니다

코드 수정이 비교적 쉽습니다

사용하시려면 다음과 같은 설치가 필요합니다:

작성중에 카카오톡 알림 설정을 위하여 토큰 발행 등 여러가지 어려움이 있었습니다.

계속 수정 중에 있습니다 .

## 2. 프로젝트 개요

- 프로그램명: 세무사견적어플 자동 견적 알리미

- 주요 기능:

- 새로운 요청 실시간 모니터링

- 데스크톱 알림 기능/알람 소리 설정

- 카카오톡 알림 연동

- 자동 견적 제출 (금액 고정 ~ 변동폭 10%)

- 맞춤형 상세 설명 메시지 자동 전송

## 3. 구현할 프로그램 상세

### 3.1 기술 스택

- 개발 언어: Python

- 주요 라이브러리:

- Selenium: 웹 자동화

- plyer: 데스크톱 알림

- requests: 카카오톡 API 연동

- webdriver_manager: 크롬드라이버 관리

### 3.2 핵심 기능

1. 자동 로그인

- 설정된 계정 정보로 자동 로그인

- 보안 로그인 처리

2. 요청 모니터링

- 30초 간격으로 새로운 요청 확인

- 요청 수 변화 감지

- 처리된 요청 목록 관리

3. 알림 시스템

- 데스크톱 팝업 알림

- 카카오톡 메시지 알림

- 현재 요청 수와 신규 요청 수 정보 제공

4. 자동 견적

- 고정 금액 견적 자동 제출

- 맞춤형 서비스 설명 메시지 전송

- 견적 전송 상태 모니터링

### 3.3 프로그램 흐름

1. 프로그램 시작 및 로그인

2. 요청 페이지 모니터링 시작

3. 새로운 요청 발견 시:

- 알림 전송 (데스크톱 + 카카오톡)

- 자동 견적 제출

- 상세 설명 메시지 전송

4. 처리 완료된 요청 기록

5. 반복 모니터링 지속

## 4.이후 상세 진행 계획

### 4.1 개선 필요 사항

현재 계속 시도중이나 실질적으로 구현이 안되고 있습니다.

어떤 부분이 문제인지 계속 검토하고 수정해야 할 듯 합니다.

1. 안정성 강화

- 네트워크 오류 대응 개선

- 세션 만료 자동 처리

- 견적 전송 실패 시 재시도 로직

2. 기능 확장

- 견적 금액 차등화 기능

- 고객별 맞춤 메시지 설정

- 통계 및 리포트 기능

3. 사용자 편의성

- GUI 인터페이스 개발

- 설정 관리 기능

- 로그 확인 및 관리 기능

4. 매크로 적발을 피할 수 있는 sleep 의 random 화

1. random delay 사용

2. human_like_typing 사용

### 4.2 향후 개발 로드맵

1. Phase 1: 현재 기능 안정화 (1주)

2. Phase 2: 오류 처리 강화 (1주)

3. Phase 3: GUI 개발 (1주)

4. Phase 4: 통계 기능 추가 (1주)

## 5. 시사점

### 5.1 기대 효과

1. 업무 효율성 향상

- 응답 시간 단축

- 24시간 모니터링 가능

- 인력 리소스 절감

2. 고객 만족도 증가

- 신속한 견적 제공

- 일관된 서비스 품질

- 상세한 정보 제공

3. 비즈니스 확장

- 처리 가능 요청 수 증가

- 전환율 향상 기대

- 서비스 영역 확대 가능

### 5.2 한계 및 과제

1. 기술적 한계

- 웹사이트 구조 변경 시 대응 필요

- 보안 정책 변경 대응 필요

- 자동화 처리의 한계

2. 서비스 품질 관리

- 자동화와 개인화 사이의 균형

- 고객 특수성 반영의 어려움

- 피드백 수집 및 반영 방안

아직 견적 버튼을 찾거나 하는 부분이 보완이 되지 않아 계속 개발중에 있습니다. 해당 프로그램을 통해 업무 효율성을 크게 향상시킬 수 있을 것으로 기대되며, 지속적인 개선과 업데이트를 통해 더 나은 서비스를 제공할 수 있을 것입니다.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from plyer import notification
import requests
import json
import logging
import random
import time
import winsound
import os
from datetime import datetime

def play_alert_sound(sound_type="default"):
    """알림 소리 재생 함수"""
    try:
        if sound_type == "default":
            winsound.PlaySound("SystemExclamation", winsound.SND_ALIAS)
        elif sound_type == "custom" and os.path.exists("alert.wav"):
            winsound.PlaySound("alert.wav", winsound.SND_FILENAME)
        else:
            winsound.Beep(1000, 500)  # 기본 비프음
    except Exception as e:
        logging.error(f"알림음 재생 중 에러: {str(e)}")

# 로깅 설정
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

def setup_driver():
    """웹드라이버 설정"""
    chrome_options = Options()
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.add_argument('--window-size=1920,1080')
    chrome_options.add_argument('--disable-blink-features=AutomationControlled')
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    
    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service, options=chrome_options)
    
    driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
    return driver

def random_delay(min_seconds=1, max_seconds=5):
    """랜덤한 시간 동안 대기"""
    delay = random.uniform(min_seconds, max_seconds)
    time.sleep(delay)
    return delay

def wait_and_find_element(driver, by, value, timeout=15, message=None, retries=3):
    """요소를 기다리고 찾는 유틸리티 함수 (재시도 로직 포함)"""
    for attempt in range(retries):
        try:
            WebDriverWait(driver, timeout).until(
                lambda x: x.execute_script("return document.readyState") == "complete"
            )
            
            element = WebDriverWait(driver, timeout).until(
                EC.presence_of_element_located((by, value))
            )
            
            driver.execute_script("arguments[0].scrollIntoView(true);", element)
            time.sleep(1)
            
            WebDriverWait(driver, timeout).until(
                EC.element_to_be_clickable((by, value))
            )
            
            return element
            
        except Exception as e:
            if attempt == retries - 1:
                logging.error(f"Element not found after {retries} attempts: {message or value}")
                logging.error(f"Error details: {str(e)}")
                raise
            logging.warning(f"Attempt {attempt + 1}/{retries} failed, retrying...")
            time.sleep(2)

def login_to_site(driver, username, password):
    """로그인 함수"""
    try:
        driver.get("YOUR_LOGIN_URL_HERE")  # 로그인 페이지 URL 추가
        random_delay(2, 4)
        
        email_input = wait_and_find_element(driver, By.CSS_SELECTOR, "input[type='email']")
        password_input = wait_and_find_element(driver, By.CSS_SELECTOR, "input[type='password']")
        
        email_input.send_keys(username)
        random_delay(0.5, 1.5)
        password_input.send_keys(password)
        random_delay(0.5, 1.5)
        
        login_button = wait_and_find_element(driver, By.CSS_SELECTOR, "button[type='submit']")
        login_button.click()
        
        # 로그인 성공 확인
        WebDriverWait(driver, 20).until(
            lambda x: any([
                "마이페이지" in x.page_source,
                "로그아웃" in x.page_source,
                len(x.find_elements(By.CSS_SELECTOR, ".user-profile")) > 0
            ])
        )
        logging.info("로그인 성공!")
        return True
        
    except Exception as e:
        logging.error(f"로그인 실패: {str(e)}")
        return False

def send_kakao_message(token, message):
    """카카오톡 메시지 전송 함수"""
    try:
        header = {'Authorization': f'Bearer {token}'}
        url = 'https://kapi.kakao.com/v2/api/talk/memo/default/send'
        data = {
            'template_object': json.dumps({
                'object_type': 'text',
                'text': message,
                'link': {
                    'web_url': 'YOUR_REQUESTS_URL_HERE',
                    'mobile_web_url': 'YOUR_REQUESTS_URL_HERE'
                }
            })
        }
        response = requests.post(url, headers=header, data=data)
        return response.status_code
    except Exception as e:
        logging.error(f"카카오톡 메시지 전송 중 에러: {str(e)}")
        return None

if __name__ == "__main__":
    # 환경변수로 설정된 값 불러오기
    username = os.getenv("USERNAME")
    password = os.getenv("PASSWORD")
    kakao_token = os.getenv("KAKAO_TOKEN")
    
    auto_message = "YOUR_AUTO_MESSAGE_HERE"  # 자동 메시지 내용 추가
    
    # 새로운 요청 확인 실행
    driver = setup_driver()
    try:
        login_to_site(driver, username, password)
    finally:
        driver.quit()
6
5개의 답글

👉 이 게시글도 읽어보세요