퍼스트가든용 기상청 API를 활용해 공지사항 자동 등록 시스템

기상정보에 따른 이벤트 진행에 대한 정확한 기준 부여를 위해 기상청 API를 사용하여, 영업시간 내 강수정보를 파악하고 자동으로 공지를 올리기 위한 프로젝트.


📁 폴더 구조

project-root/
├── logs/                          # 크론 실행 로그 저장 경로
│   └── cron.log                   # Crontab 실행 로그
│
├── data/                          # SQLite DB, 캡처 이미지 저장 경로 (공용 볼륨)
│   ├── weather.sqlite             # 날씨 DB (precipitation, summary 테이블)
│   └── weather_capture_YYYYMMDD.png  # 일자별 날씨 캡처 이미지
│
├── app/                          # gnu-autouploader + API 서버 (통합)
│   ├── gnu_autoupload.py         # 메인 실행 스크립트 (Selenium → FTP → DB)
│   ├── weather_capture.py        # Selenium 기반 날씨 이미지 캡처 + 강우량 추출
│   ├── weather.py                # 기상청 API 데이터 처리 및 sqlite 저장
│   ├── send_message.py           # Mattermost 알림 발송
│   ├── selenium_manager.py       # Selenium 브라우저 관리
│   ├── api_server.py             # Flask 기반 카카오 챗봇 웹훅 서버 ⭐
│   ├── config.py                 # 설정값 로드 (DB, FTP, API KEY 등)
│   ├── requirements.txt          # Python 의존성
│   └── run.sh                    # 수동 실행용 셸 스크립트 (개발 시 사용)
│
├── webhook/                      # (더 이상 사용 안 함 - app/api_server.py로 통합)
│   └── webhook.py                # (참고용 아카이브)
│
├── build/
│   ├── app/
│   │   ├── Dockerfile            # gnu-autouploader + Flask 통합 이미지
│   │   ├── entrypoint.sh         # Flask + Cron 동시 실행 스크립트 ⭐
│   │   └── run.sh                # (위의 app/run.sh와 동일)
│   └── webhook/
│       └── Dockerfile            # (더 이상 사용 안 함)
│
├── .env.example                  # 환경 변수 템플릿 (.env로 복사하여 수정)
├── docker-compose.yml            # Docker Compose 서비스 정의 (gnu-autouploader만)
└── README.md                     # 프로젝트 문서

📋 주요 스크립트 설명

app/gnu_autoupload.py (메인)

  • 역할: 날씨 캡처 → FTP 업로드 → 그누보드 DB에 게시글 등록
  • 실행 방식:
    • 매일 09:00 Crontab 자동 실행
    • docker exec 또는 run.sh로 수동 실행 가능
  • 오류 발생 시: Mattermost으로 알림 발송

app/weather_capture.py (개선)

  • 이전: Selenium으로 웹페이지 캡처만 수행
  • 현재:
    • Selenium으로 기상청 웹페이지 접근
    • 페이지에서 강우량 데이터 자동 추출 (10시~21시)
    • '-'는 0mm, '~1'은 0.5mm로 자동 계산
    • 추출된 강우량을 SQLite에 저장 (rainfall_capture, rainfall_summary 테이블)
    • 웹페이지 이미지 캡처 저장

app/weather.py

  • 기상청 API에서 시간별 강수량 데이터 수집
  • 10:00 ~ 22:00 영업시간 강수 데이터 HTML 테이블 생성
  • SQLite DB에 저장

app/api_server.py (Flask 웹훅 서버)

  • Flask 기반 카카오 챗봇 응답 서버
  • 포트: 5000 (docker-compose에서 5151로 노출)
  • 주요 기능:
    • 당일 조회: 09:00 캡처된 실제 강우량 데이터 응답
    • 미래 날짜 조회: 기상청 API 기반 예보 강우량 응답
    • 예보 시 "변동될 수 있음" 경고 문구 표시
    • 10mm 초과 시 이벤트 적용 안내
    • 날씨 캡처 이미지 함께 전송
  • 주요 엔드포인트:
    • POST /webhook - Kakaotalk 챗봇 웹훅
    • GET /health - 헬스 체크
    • GET /data/<filename> - 캡처 이미지 조회

app/config.py

  • 환경 변수 로드 (.env 또는 컨테이너 환경 변수)
  • 필수 변수 부재 시 즉시 오류 출력 후 종료

🚀 설치 & 실행

1. 환경 설정

# .env 파일 생성
cp .env.example .env

# 필수 정보 입력
vim .env

필수 환경 변수

# 게시판 정보
BOARD_ID=news
BOARD_CA_NAME=카테고리명
BOARD_CONTENT=글 내용
BOARD_MB_ID=user_id
BOARD_NICKNAME=닉네임

# MySQL 연결 (로컬: localhost, Docker: db 서비스명, Synology: 실제 호스트 IP/도메인)
DB_HOST=localhost
DB_USER=db_user
DB_PASSWORD=db_password
DB_NAME=database_name

# FTP 업로드
FTP_HOST=ftp.example.com
FTP_USER=ftp_user
FTP_PASSWORD=ftp_password
FTP_UPLOAD_DIR=/data/file/news/

# 기상청 API
SERVICE_KEY=your_api_key_here

# Mattermost 알림 (선택사항)
MATTERMOST_URL=https://mattermost.example.com
MATTERMOST_TOKEN=token
MATTERMOST_CHANNEL_ID=channel_id

2. Docker Compose 실행

# 빌드 및 실행
docker-compose up -d

# 로그 확인
docker-compose logs -f gnu-autouploader

# 크론 실행 로그 확인
docker exec gnu-autouploader tail -f /logs/cron.log

3. 수동 실행

# 컨테이너에서 직접 실행
docker exec -it gnu-autouploader /usr/bin/python /app/gnu_autoupload.py

# 또는 run.sh 사용
docker exec -it gnu-autouploader /app/run.sh


🎯 시스템 동작 플로우

아키텍처 (통합 구조)

[gnu-autouploader 컨테이너] (포트 5151:5000 노출)
├─ Crontab 데몬 (포그라운드)
│  └─ 매일 09:00 크론 작업 실행
│
└─ Flask 웹서버 (백그라운드)
   └─ 포트 5000에서 지속 실행

일일 작업 플로우

매일 09:00
    ↓
[Crontab 작업 시작]
    ├─ 1. weather_capture.py 실행
    │   ├─ Selenium으로 기상청 웹페이지 접근
    │   ├─ 강우량 데이터 추출 (10시~21시) ⭐
    │   ├─ SQLite 저장
    │   └─ 웹페이지 이미지 캡처 저장
    ├─ 2. weather.py 실행 (선택적)
    │   └─ API 기반 시간별 강수 데이터 저장
    └─ 3. gnu_autoupload.py 실행
        ├─ 캡처된 이미지 FTP 업로드
        └─ 그누보드 게시글 자동 등록

동시에 실행 중: Flask 웹서버
    ↓ (사용자 요청 시)
[Flask 웹훅 엔드포인트]
    ├─ "오늘의 강우량은?" → SQLite에서 실제 데이터 응답 ✓
    ├─ "내일 강우량은?" → API 예보 데이터 + 경고 문구 응답 ⚠️
    └─ "강우량 10mm 초과?" → 이벤트 적용 여부 자동 판단

🗄️ SQLite 데이터베이스 스키마

rainfall_capture (웹페이지 캡처 데이터)

CREATE TABLE rainfall_capture (
    id INTEGER PRIMARY KEY,
    date TEXT,           -- 'YYYYMMDD'
    hour INTEGER,        -- 10~21 (10시~21시)
    rainfall REAL        -- mm 단위
);

rainfall_summary (일일 합계)

CREATE TABLE rainfall_summary (
    id INTEGER PRIMARY KEY,
    date TEXT UNIQUE,    -- 'YYYYMMDD'
    total_rainfall REAL, -- mm 단위
    capture_time TEXT    -- '2025-12-19 09:00:00'
);

<EFBFBD> 웹훅 API 사용법

웹훅 엔드포인트

URL: https://webhook.firstgarden.co.kr/webhook (또는 DOMAIN/webhook)

요청 방식: POST

1. 카카오 챗봇 연동 (자동)

요청 형식 (카카오로부터)

{
    "userRequest": {
        "text": "12월 19일 강우량은?",
        "user": {
            "id": "user_123",
            "properties": {}
        }
    }
}

응답 형식 (카카오에게)

{
    "version": "2.0",
    "template": {
        "outputs": [
            {
                "simpleText": {
                    "text": "📅 12월 19일(금)...(응답 내용)..."
                }
            },
            {
                "simpleImage": {
                    "imageUrl": "https://webhook.firstgarden.co.kr/data/weather_capture_20251219.png",
                    "altText": "날씨 이미지"
                }
            }
        ]
    }
}

2. 직접 API 호출 (수동 테스트)

cURL로 테스트

# 당일 조회
curl -X POST https://webhook.firstgarden.co.kr/webhook \
  -H "Content-Type: application/json" \
  -d '{"userRequest": {"text": "오늘 레이니데이 적용?"}}'

# 특정 날짜 조회
curl -X POST https://webhook.firstgarden.co.kr/webhook \
  -H "Content-Type: application/json" \
  -d '{"userRequest": {"text": "12월 20일 레이니데이"}}'

# 헬스 체크
curl https://webhook.firstgarden.co.kr/health

Python으로 테스트

import requests

webhook_url = "https://webhook.firstgarden.co.kr/webhook"

payload = {
    "userRequest": {
        "text": "내일 레이니데이?"
    }
}

response = requests.post(webhook_url, json=payload)
print(response.json())

3. 응답 분석 규칙

챗봇은 사용자 입력에서 날짜 패턴을 자동으로 감지합니다:

입력 예시 인식 날짜 데이터 출처
"오늘 강우량" 당일 SQLite (09:00 캡처) 실제값
"12월 19일" 2025-12-19 당일이면 SQLite, 미래면 API ⚠️
"내일" 내일 API 예보
"모레" 모레 API 예보
"12월 25일" 2025-12-25 API 예보
"날짜 지정 없음" 당일 SQLite (09:00 캡처)

4. 웹훅 응답 예시

당일 실제 데이터 (SQLite)

📅 12월 19일(금)
📊 실제 강수량 (09:00 캡처 기준)

10:00 → ☀️ 강수 없음
11:00 → ☀️ 강수 없음
12:00 → 0.5mm
13:00 → 0.5mm
14:00 → 1.2mm
...
21:00 → 2.3mm

💧 총 강수량: 5.2mm
❌ 이벤트 기준(10mm 초과)을 충족하지 않음

🔗 게시글 링크: https://firstgarden.co.kr/news/123

미래 날짜 예보 (API)

📅 12월 20일(토)
📊 예보 강수량 (08:00 발표 기준)

10:00 → 1.2mm
11:00 → 2.1mm
12:00 → 3.5mm
...
21:00 → 0.8mm

💧 총 강수량: 12.5mm
✅ 레이니데이 적용 가능

⚠️ 이는 기상청 08:00 발표 예보입니다. 실제 이벤트 적용 기준은 당일 09:00 캡처 데이터입니다.

이미지 첨부 (자동)

응답에 자동으로 캡처된 날씨 이미지가 함께 전송됩니다.

5. 헬스 체크

# 엔드포인트 상태 확인
curl https://webhook.firstgarden.co.kr/health

# 응답
{
    "status": "healthy",
    "timestamp": "2025-12-19 10:30:45"
}

6. 이미지 직접 조회

# 캡처 이미지 조회 (브라우저에서도 열기 가능)
https://webhook.firstgarden.co.kr/data/weather_capture_20251219.png

🎯 카카오 챗봇 설정

1. 카카오 디벨로퍼 콘솔에서

  1. 카카오 디벨로퍼 콘솔 접속
  2. 앱 생성 → "채팅" 선택
  3. 구성 > 채팅 설정 이동
  4. 스킬 추가 클릭
    • 스킬명: 레이니데이 이벤트
    • URL: https://webhook.firstgarden.co.kr/webhook
    • HTTP 메서드: POST
  5. 인텐트 설정 (예시)
    • "강우량은?", "날씨는?", "이벤트 조건?", "무료 입장?", "강수량", "강우" 등

2. 테스트 채널에서 확인

  • 카카오톡 채팅 → 챗봇에 메시지 전송 → 웹훅이 자동으로 응답

📊 게시글 등록 완료 알림

gnu_autoupload.py가 성공적으로 게시글을 등록할 때, Mattermost 알림이 발송됩니다:

✅ **게시글 등록 완료**

📅 날짜: 2025-12-19 09:05:30
📋 게시판: `news`
📝 제목: 2025-12-19 날씨정보
📎 첨부파일: 2개
🖼️ 캡처파일: `weather_capture_20251219.png` (1024.5KB)
🔗 게시글 링크: https://firstgarden.co.kr/news/123

포함 정보:

  • 등록 일시
  • 게시판 ID
  • 게시글 제목
  • 첨부파일 개수
  • 캡처 이미지 정보
  • 게시글 직접 링크 (URL/BOARD_ID/wr_id 형식)

<EFBFBD>💬 카카오 챗봇 응답 예시

당일 조회 (실제 데이터)

📅 12월 19일(금)
📊 실제 강수량 (09:00 캡처 기준)

10:00 → ☀️ 강수 없음
11:00 → ☀️ 강수 없음
12:00 → 0.5mm
...
21:00 → 2.3mm

💧 총 강수량: 5.2mm
❌ 이벤트 기준(10mm 초과)을 충족하지 않음

[날씨 캡처 이미지]

미래 날짜 조회 (API 예보)

📅 12월 20일(토)
📊 예보 강수량 (08:00 발표 기준)

10:00 → 1.2mm
11:00 → 2.1mm
...
21:00 → 0.8mm

💧 총 강수량: 12.5mm
✅ 식음료 2만원 이상 결제 시 무료입장권 제공

⚠️ 이는 기상청 08:00 발표 예보입니다. 실제 이벤트 적용 기준은 당일 09:00 캡처 데이터입니다.

⚙️ 크론탭 설정

Docker 컨테이너 내부에서 매일 09:00에 자동 실행됩니다.

0 9 * * * /usr/bin/python /app/gnu_autoupload.py >> /logs/cron.log 2>&1

로그 확인:

docker exec gnu-autouploader tail -f /logs/cron.log

🔍 문제 해결

MySQL 연결 오류

  • 오류: Can't connect to MySQL server on 'localhost'
  • 원인: Docker 컨테이너에서 localhost는 컨테이너 자신을 가리킴
  • 해결:
    • Docker 환경: DB_HOST=db (docker-compose 서비스명)
    • Synology: DB_HOST=192.168.x.x (호스트 IP)

Mattermost 알림 실패

  • 오류: Invalid URL '/api/v4/posts': No scheme supplied
  • 원인: URL에 http:// 또는 https://가 없음
  • 해결: MATTERMOST_URL=https://mattermost.example.com 명시

크론탭에서 환경 변수 미로드

  • 원인: 기존 버전에서 crontab이 .env 파일 접근 불가
  • 해결: 현재는 docker-compose.yml에서 .env를 volume mount + env_file로 처리

📝 설정 변경 후 재배포

# 컨테이너 재시작
docker-compose restart gnu-autouploader

# 또는 재빌드
docker-compose up -d --build

🛠️ 개발 & 디버깅

로컬 테스트 (가상환경)

# 가상환경 생성
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# 의존성 설치
pip install -r app/requirements.txt

# .env 파일 생성 후 직접 실행
python app/gnu_autoupload.py

Docker 내 수동 실행

# 날씨 캡처 + 강우량 추출
docker exec gnu-autouploader /usr/bin/python /app/weather_capture.py

# 메인 작업 (게시글 등록)
docker exec gnu-autouploader /usr/bin/python /app/gnu_autoupload.py

# 기상청 API 데이터 (선택사항)
docker exec gnu-autouploader /usr/bin/python /app/weather.py

로그 확인

# Crontab + Flask 통합 로그
docker-compose logs -f gnu-autouploader

# Crontab 실행 로그만
docker exec gnu-autouploader tail -f /logs/cron.log

# Flask 웹서버 로그
docker exec gnu-autouploader tail -f /logs/flask.log
Description
퍼스트가든 업무 자동화
Readme 831 KiB
Languages
Python 95.9%
Dockerfile 2.6%
Shell 1.5%