## 퍼스트가든용 기상청 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/` - 캡처 이미지 조회 ### `app/config.py` - 환경 변수 로드 (`.env` 또는 컨테이너 환경 변수) - 필수 변수 부재 시 즉시 오류 출력 후 종료 --- ## 🚀 설치 & 실행 ### 1. 환경 설정 ```bash # .env 파일 생성 cp .env.example .env # 필수 정보 입력 vim .env ``` ### 필수 환경 변수 ```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 실행 ```bash # 빌드 및 실행 docker-compose up -d # 로그 확인 docker-compose logs -f gnu-autouploader # 크론 실행 로그 확인 docker exec gnu-autouploader tail -f /logs/cron.log ``` ### 3. 수동 실행 ```bash # 컨테이너에서 직접 실행 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` (웹페이지 캡처 데이터) ```sql CREATE TABLE rainfall_capture ( id INTEGER PRIMARY KEY, date TEXT, -- 'YYYYMMDD' hour INTEGER, -- 10~21 (10시~21시) rainfall REAL -- mm 단위 ); ``` ### `rainfall_summary` (일일 합계) ```sql 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' ); ``` --- ## � 웹훅 API 사용법 ### 웹훅 엔드포인트 **URL**: `https://webhook.firstgarden.co.kr/webhook` (또는 `DOMAIN/webhook`) **요청 방식**: `POST` ### 1. 카카오 챗봇 연동 (자동) #### 요청 형식 (카카오로부터) ```json { "userRequest": { "text": "12월 19일 강우량은?", "user": { "id": "user_123", "properties": {} } } } ``` #### 응답 형식 (카카오에게) ```json { "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로 테스트 ```bash # 당일 조회 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으로 테스트 ```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. 헬스 체크 ```bash # 엔드포인트 상태 확인 curl https://webhook.firstgarden.co.kr/health # 응답 { "status": "healthy", "timestamp": "2025-12-19 10:30:45" } ``` ### 6. 이미지 직접 조회 ```bash # 캡처 이미지 조회 (브라우저에서도 열기 가능) https://webhook.firstgarden.co.kr/data/weather_capture_20251219.png ``` --- ## 🎯 카카오 챗봇 설정 ### 1. 카카오 디벨로퍼 콘솔에서 1. [카카오 디벨로퍼 콘솔](https://developers.kakao.com/) 접속 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` 형식) --- ## �💬 카카오 챗봇 응답 예시 ### 당일 조회 (실제 데이터) ``` 📅 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 ``` **로그 확인:** ```bash 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로 처리 --- ## 📝 설정 변경 후 재배포 ```bash # 컨테이너 재시작 docker-compose restart gnu-autouploader # 또는 재빌드 docker-compose up -d --build ``` --- ## 🛠️ 개발 & 디버깅 ### 로컬 테스트 (가상환경) ```bash # 가상환경 생성 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 내 수동 실행 ```bash # 날씨 캡처 + 강우량 추출 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 ``` ### 로그 확인 ```bash # 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 ```