보람찬 코기의 개발자 블로그
article thumbnail
반응형

AI 제작 : 로또 번호 조회 및 추천 번호를 생성 api

📌 개요

해당 글에서는 FastAPI를 활용하여 로또 번호 조회 및 생성 API를 개발에 대해 기술하고,
API 기능 구현 과정을 기록한다.

 

소요기간 1일

'25.02.17

 

Git repo는 다음 링크과 같다.

https://github.com/WellshCorgi/dev-infinity-api

 

GitHub - WellshCorgi/dev-infinity-api: A project focused on continuously developing various features, designing efficient APIs,

A project focused on continuously developing various features, designing efficient APIs, and documenting the process along the way. - WellshCorgi/dev-infinity-api

github.com


📌 프로젝트 구조

.
├── API_Readme
│   ├── Lotto_README.md
├── LICENSE
├── README.md
├── app
│   ├── __pycache__
│   │   └── main.cpython-310.pyc
│   ├── core
│   │   ├── config.py
│   │   ├── lotto_generate.py
│   └── routers
│       ├── lotto.py
├── main.py
├── requirements.txt

 

 


📌 구현 특징

로깅 시스템 적용

  • logging을 활용하여 에러 발생 시 로그 기록
  • API 요청 및 응답 흐름을 추적하여 디버깅 및 유지보수 용이

예외 처리 및 HTTP 응답 코드 활용

  • 예상 가능한 오류 (네트워크 문제, 잘못된 입력 등)에 대해 try-except를 활용하여 HTTPException을 반환
  • 500 에러 (서버 문제)와 404 에러 (회차 정보 없음)를 구분하여 사용자에게 적절한 피드백 제공

랜덤 번호 생성 최적화

  • random.sample()을 활용하여 중복 없는 숫자 생성
  • 리스트 내포(List Comprehension) 사용으로 코드 간결화

실제 동행복권 API 연동

  • requests를 사용하여 동행복권 API에서 최신 당첨 번호를 실시간 조회
  • HTML 파싱을 통해 가장 최근 회차를 자동으로 감지하여 API 요청

📌 주요 기능

로또 번호 생성 (/lotto/gen)

  • 기능: 사용자가 요청한 개수만큼 랜덤한 로또 번호(6개)와 보너스 번호(1개)를 생성
  • 제약 사항: 한 번에 1~5세트까지 요청 가능
  • 구현 방식:
    • random.sample()을 사용하여 1~45 사이의 숫자 중 6개를 추출
    • 보너스 번호는 이미 선택된 숫자를 제외한 나머지 중에서 랜덤 선택
    • 최신 로또 당첨 번호를 함께 제공
def generate_num():
    numbers = random.sample(range(1, 45), 6)
    numbers.sort()
    bonus = random.choice([num for num in range(1, 46) if num not in numbers])
    return numbers, bonus



@router.get("/gen")
async def generate_lotto(sets: int = Query(1, ge=1, le=5)):
    try:
        results = [
            {"numbers": numbers, "bonus": bonus}
            for numbers, bonus in (generate_num() for _ in range(sets))
        ]
    except Exception as e:
        logger.error(f"Error generating lotto numbers: {str(e)}")
        raise HTTPException(status_code=500, detail="Internal server error")

    try:
        latest_results = get_lotto_winning_number(None)
    except Exception as e:
        logger.error(f"Error retrieving latest lotto results: {str(e)}")
        raise HTTPException(status_code=500, detail="Failed to retrieve latest results")

    return {
        "message": "Lotto numbers generated successfully",
        "timestamp": datetime.datetime.now().isoformat(),
        "generated_numbers": results,
        "latest_draw": latest_results
    }

 

요청 결과

 

 

 

 

 

특정 회차 로또 당첨 번호 조회 (/lotto/search)

  • 기능: 특정 회차의 당첨 번호를 가져오는 API
  • 구현 방식:
    • requests를 이용해 동행복권 API에서 데이터를 가져온다
    • 최신 회차 번호를 가져오려면 메인 페이지에서 lottoDrwNo 값을 파싱
    • API 요청을 통해 해당 회차의 당첨 번호 JSON을 가져와 가공
def get_lotto_winning_number(round_num: int = None):
    """특정 회차의 로또 당첨 번호를 가져오는 함수. 디폴트값은 최신 당첨 번호"""
    try:
        if round_num is None:
            lotto_main_page = requests.get("https://dhlottery.co.kr/common.do?method=main").text
            latest_draw = int(lotto_main_page.split('<strong id="lottoDrwNo">')[1].split('</strong>')[0])

            response = requests.get(f"https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo={latest_draw}")
            data = response.json()

            if data["returnValue"] != "success":
                raise Exception("Failed to fetch latest winning number")

            latest_numbers = [data[f"drwtNo{i}"] for i in range(1, 7)]
            bonus_number = data["bnusNo"]

            return {
                "draw_no": latest_draw,
                "draw_date": data["drwNoDate"],
                "numbers": latest_numbers,
                "bonus": bonus_number
            }
        else:
            response = requests.get(f"https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo={round_num}")
            data = response.json()

            if data["returnValue"] == "success":
                latest_numbers = [data[f"drwtNo{i}"] for i in range(1, 7)]
                bonus_number = data["bnusNo"]
                return {
                    "draw_no": round_num,
                    "draw_date": data["drwNoDate"],
                    "numbers": latest_numbers,
                    "bonus": bonus_number
                }
            else:
                raise Exception(f"Could not find results for round {round_num}")
    except Exception as e:
        logger.error(f"Error fetching lotto results for round {round_num}: {str(e)}")
        return None

 

요청 결과

 

 


📌 주요 과정

구현하기 위해 기본 제공하는 api가 있는지 확인했더니 다음 링크에서 제공하고 있었다.

https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo=1000

 

https://www.dhlottery.co.kr/common.do?drwNo=1000&method=getLottoNumber

 

www.dhlottery.co.kr

 

동행복권에서 제공하는 api

 

 

url 주소 뒷부분에 파라미터가 존재하길래 이번 기능 구현은 쉬울 것이라고 생각하였다.

 

제공되는 해당 주소에 뒷부분 파라미터만 수정하여 요청보내고 반환된 값을 가공하자는 목표를 두고 개발을 하였다.

 

 

사용자가 해당 파라미터를 입력하지않으면 디폴트 값으로 최신 회차 번호를 설정해두고자 하였다.

 

그래서 크롤링을 활용하여 최신회차 번호를 파싱했다.

 

해당 숫자는 동행복권 사이트의 소스검사를 통해 해당 요소인  <strong id="lottoDrwNo"> 에서 추출할 수 있었다.

 


📌 결론

 

동행복권 API를 직접 파싱하고 데이터를 재가공하여 보다 흥미로운 방식으로 구현해보았다.

 

하지만 해당 API 서비스가 중지된다면 치명적인 문제가 발생할 가능성이 크다고 판단했다.

 

이에 대한 대응책으로는 데이터베이스(DB)를 구축하고 직접 쿼리를 활용하는 방식이 적절할 것이라 생각한다.

 

이를 통해 API 의존성을 줄이고, 더 안정적인 서비스를 운영할 수 있을 것이다.

 

추가적으로, 사용자들의 로또 번호를 저장하고, 추첨 결과와 비교하여 당첨 여부를 자동으로 모니터링하여 나에게 알람 보내주는 시스템을 구축하면 더욱 흥미로운 기능이 될 것 같다. 

 

또한, FastAPI를 사용하면서 가장 만족스러웠던 점 중 하나는 주석만 달아도 자동으로 Swagger 문서가 생성된다는 것이다. 덕분에 문서화에 대한 부담이 크게 줄어들었고, 개발 과정에서의 피로도를 낮출 수 있었다. 강력 추천!

 

 

 

 

그리고 GitHub Actions를 활용하여 라즈베리파이 기반 K8s 클러스터에 CI/CD를 구축하고 자동 배포까지 진행할 계획이다. 이를 통해 개발과 배포 프로세스를 더욱 효율적으로 만들어 볼 것이다.

반응형
profile

보람찬 코기의 개발자 블로그

@BoChan

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!