본문 바로가기
Python

[Python] 오픈 API를 이용해 공휴일 정보 받아오기

by 태진아밴드 2021. 7. 22.

몇 달전 회사에서 서비스하고 있는 프로젝트에 국가 공휴일이 반영되어야 하는 일이 있었다.

 

국가 공휴일관련 테이블을 새로 만들어서 데이터를 향후 10년치를 밀어 넣어둘까 하다가

 

중간중간 변동이 생길때마다 업데이트 해주는게 번거로울거 같아

 

공공 데이터를 API로 받아와서 매일 한번씩 배치를 돌리는 방향으로 가기로 결정되었다.

 

간단하게 데이터만 받아오면 되는 프로그램이라 파이썬으로 작성하여 운영중인데 작업 과정을 적어보려고한다.

 

우선은 공공데이터 포털에 들어가서 회원가입을 진행하자.

 

https://www.data.go.kr/index.do

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

 

회원가입을 완료했다면 이제 공휴일관련 API 를 활용신청하러 가보자

 

https://www.data.go.kr/data/15012690/openapi.do

 

한국천문연구원_특일 정보

(천문우주정보)국경일정보, 공휴일정보, 기념일정보, 24절기정보, 잡절정보를 조회하는 서비스 입니다.

www.data.go.kr

 

여기서 활용신청을 하고 서비스키를 발급받아야 API 이용이 가능하니 키를 발급 받아야 한다.

 

( 최대 1시간 정도 걸릴 수 있다고 하니 작업전에 미리 발급받아두는게 편하다 )

 

활용 승인이 났다면 포스트맨으로 먼저 테스트 호출을 해보자.

 

{
    "response": {
        "header": {
            "resultCode": "00",
            "resultMsg": "NORMAL SERVICE."
        },
        "body": {
            "items": {
                "item": {
                    "dateKind": "01",
                    "dateName": "제헌절",
                    "isHoliday": "N",
                    "locdate": 20210717,
                    "remarks": "국경일이지만 공휴일 아님",
                    "seq": 1
                }
            },
            "numOfRows": 10,
            "pageNo": 1,
            "totalCount": 1
        }
    }
}

응답을 살펴보면 dateName 부분은 공휴일 명칭, isHoliday 부분은 실제로 휴무일인지 여부 locdate는 날짜를 나타낸다.

 

우선 DB에 공휴일 관련 테이블로 필요한 데이터들만 넣게끔 심플하게 테이블을 작성해주자.

-- 공휴일 관련 테이블 생성
CREATE TABLE `TB_NATIONAL_HOLIDAYS` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `holiday_dt` varchar(8) NOT NULL COMMENT '날짜',
  `day_name` varchar(32) NOT NULL COMMENT '휴일명',
  `is_holiday` varchar(1) NOT NULL COMMENT '공휴일인데 휴무 여부',
  PRIMARY KEY (`id`),
  KEY `IDX_NATIONAL_HOLIDAYS_01` (`holiday_dt`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT='국가 공휴일 테이블'

 

이제 파이썬으로 코드를 작성해보자 ( 참고로 해당 코드는 파이썬 3.8 기준으로 작성되었다 )

 

import sys
import traceback
import datetime as date
import MySQLdb as mdb
import requests

############################
# 공휴일 배치
############################

# 요청 url 리턴해주는 함수
def get_request_query(url, operation, params, serviceKey, type):
    import urllib.parse as urlparse
    params = urlparse.urlencode(params)
    request_query = url + '/' + operation + '?' + params + '&' + 'serviceKey' + '=' + serviceKey + '&' + '_type' + '=' + type
    return request_query

try:

    # 현재년도 가져오기
    today = date.datetime.now()
    today_year = today.strftime('%Y')

    print('[' + str(date.datetime.now().strftime('%Y.%M.%d %H:%M:%S')) + ']' + ' 공휴일 배치 시작')

    # 2년간 데이터만 조회되므로 내년 데이터까지만 확인
    search_year = [int(today_year), int(today_year) + 1]
    search_month = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']

    # 공휴일 요청 API URL
    URL = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService'
    # 국경일 + 공휴일 정보 조회
    OPERATION = 'getHoliDeInfo'
    # 서비스키
    SERVICEKEY = '발급 받은 서비스키'
    # 데이터 타입
    TYPE = 'json'

    # DB에 넣을 데이터
    result_date = []
    result_name = []
    result_is_holiday = []

    # API 요청
    for year in search_year:
        # 파라미터
        for month in search_month:
            PARAMS = {'solYear': year, 'solMonth': month}

            request_query = get_request_query(URL, OPERATION, PARAMS, SERVICEKEY, TYPE)
            response = requests.get(url=request_query)
            items = response.json().get('response').get('body').get('items')

            # 데이터가 있을때만
            if (items != ''):
                holidays = items.get('item')

                # 타입체크
                if ( type(holidays) == type({})):
                    day_name = holidays.get('dateName')
                    is_holiday = holidays.get('isHoliday')
                    holiday_dt = holidays.get('locdate')

                    result_date.append(str(holiday_dt))
                    result_name.append(day_name)
                    result_is_holiday.append(is_holiday)
                elif (type(holidays) == type([])):
                    for holiday in holidays:
                        day_name = holiday.get('dateName')
                        is_holiday = holiday.get('isHoliday')
                        holiday_dt = holiday.get('locdate')

                        result_date.append(str(holiday_dt))
                        result_name.append(day_name)
                        result_is_holiday.append(is_holiday)

    # DB 접속
    conn = mdb.connect("DB 접속 IP","접속 ID","접속 PW","name")
    cur = conn.cursor(mdb.cursors.DictCursor)

    cur.execute("SET NAMES utf8")
    cur.execute("SET CHARACTER SET utf8")
    cur.execute("SET character_set_connection=utf8")

    # 기존 데이터 삭제
    sql = "TRUNCATE TB_NATIONAL_HOLIDAYS"
    cur.execute(sql)

    insert_count = 0;
    # 공휴일 정보 입력
    for i in range(len(result_date)):
        sql = "INSERT INTO TB_NATIONAL_HOLIDAYS (holiday_dt, day_name, is_holiday) VALUES (%s, %s, %s)"
        val = (result_date[i], result_name[i], result_is_holiday[i])
        cur.execute(sql, val)
        insert_count += 1

    print('[' + str(insert_count) +'건 등록]')
    print('[' + str(date.datetime.now().strftime('%Y.%M.%d %H:%M:%S')) + ']' + ' 공휴일 배치 종료')

    conn.commit()

except Exception as ex:
    if conn:
        conn.rollback()
        conn.commit()
    print ("Error " , ex)
    print (traceback.format_exc())
    sys.exit(1)

 

호출 테스트를 해보니 현재일 기준으로 내년까지만 조회가 되어서 현재년도와 내년치 공휴일만 저장하는 형태로 구현하였다.

 

이제 해당 파일을 크론탭에 등록시켜 매일 아침 9시에 실행되게끔 설정해보자.

 

우선 터미널을 켜고 크론탭을 설정하자.

 

crontab -e 를 입력하면 편집할 수 있는 화면이 나오는데 아래와 같이 입력해주자

# 매일 09:00  공휴일 데이터 입력
0 9 * * * /bin/python /실제 파일경로/파일명.py > /dev/null 2>&1

저장하고 난 뒤 크론탭을 확인해보면 정상적으로 등록된 걸 확인할 수 있다. 

 

설정하고 난 뒤 service cron restart 명령어를 통해 재시작을 해주면 정상적으로 작동하게 된다.

 

간단하게 만들어 둔거라 DB 접속정보라던지 이런것들이 python 파일 안에 그대로 하드코딩 되어있는데 

 

접속정보를 별도로 분리해서 읽어오게 한다던지 이런 부분들은 추후에 고려하면 될거같다.