# HUFS 컴퓨터공학부 공지 크롤러 `HUFS 컴퓨터공학부` 사이트의 다음 게시판을 크롤링하는 `FastAPI` 백엔드입니다. - 공지사항 - 자료실 - 취업정보 `n8n`이 `POST /api/v1/crawl`을 주기적으로 호출하면, 서버는 게시판을 다시 확인하고 `PostgreSQL`에 저장된 기존 글과 비교해 새 글만 반환합니다. ## 문서 - 서비스 설명: [`README.md`](/C:/Users/USER/Desktop/notice_crawler/README.md) - 운영/배포: [`README.operation.md`](/C:/Users/USER/Desktop/notice_crawler/README.operation.md) - 테스트: [`README.test.md`](/C:/Users/USER/Desktop/notice_crawler/README.test.md) - n8n 연동: [`README.n8n.md`](/C:/Users/USER/Desktop/notice_crawler/README.n8n.md) ## 주요 기능 - 공지사항, 자료실, 취업정보 게시판 크롤링 - 게시판별 `article_id` 기준 신규 글 판별 - 제목, 작성자, 게시일, 본문 텍스트, 첨부파일 링크 정리 - 사용자에게 보이는 `subview.do?enc=...` 링크 반환 - 최초 1회 실행 시 `bootstrap mode`로 기존 글 알림 폭주 방지 - `new_posts_count == 0`일 때만 `latest_posts_by_board` 제공 - Docker Hub 이미지 배포 및 `docker compose pull` 운영 지원 ## 동작 방식 1. `n8n`이 `POST /api/v1/crawl` 요청을 보냅니다. 2. 서버가 세 게시판 목록 페이지를 크롤링합니다. 3. 각 게시글의 `article_id`를 DB와 비교합니다. 4. DB에 없는 글만 상세 페이지를 추가 크롤링합니다. 5. 정리된 데이터를 응답으로 반환하고 DB에 저장합니다. 6. 다음 실행부터는 이미 저장된 글은 제외됩니다. ## bootstrap mode 최초 실행 시 예전 글 알림이 한꺼번에 나가는 것을 막기 위해 `bootstrap mode`를 사용합니다. 판단 기준: - `scraped_posts` 테이블이 비어 있으면 `bootstrap_mode = true` - 저장된 글이 하나라도 있으면 `bootstrap_mode = false` 동작: - `bootstrap_mode = true` - 기존 글을 DB에 저장만 함 - `new_posts_count = 0` - `new_posts = []` - 이후부터는 일반 신규 감지 모드로 동작 즉, 첫 실행에서 기존 공지가 한꺼번에 Discord/Slack으로 쏟아지는 문제를 막습니다. ## API ### `GET /health` 서버 상태 확인용입니다. 응답: ```json { "status": "ok" } ``` ### `POST /api/v1/crawl` 세 게시판을 크롤링해 새 글만 반환합니다. 응답 필드: - `checked_at`: 크롤링 시각 - `bootstrap_mode`: bootstrap 실행 여부 - `bootstrap_inserted_count`: bootstrap 시 저장된 글 수 - `new_posts_count`: 실제 신규 글 수 - `new_posts`: 신규 글 목록 - `latest_posts_by_board`: 게시판별 최신 글 - `new_posts_count == 0`일 때만 포함 - 별도 추가 요청이 아니라 실제 크롤링 결과를 재사용 응답 예시: ```json { "checked_at": "2026-03-17T00:00:00Z", "bootstrap_mode": false, "bootstrap_inserted_count": 0, "new_posts_count": 1, "new_posts": [ { "board_key": "notice", "board_name": "공지사항", "board_id": 1926, "article_id": 249714, "title": "예시 제목", "post_url": "https://computer.hufs.ac.kr/computer/10058/subview.do?enc=...", "author": "computer", "published_at": "2026-03-17T00:00:00", "summary": "본문 요약", "content_text": "정리된 본문 텍스트", "attachments": [ { "name": "첨부파일.pdf", "url": "https://computer.hufs.ac.kr/..." } ] } ], "latest_posts_by_board": [] } ``` ## 환경 변수 `.env.example`을 참고해서 `.env`를 준비합니다. ```env APP_ENV=production DB_USER=postgres DB_PASSWORD=postgres POSTGRES_HOST=postgres POSTGRES_PORT=5432 POSTGRES_DB=hufs_notice_crawler BASE_URL=https://computer.hufs.ac.kr REQUEST_TIMEOUT_SECONDS=15 MAX_PAGES_PER_BOARD=5 DOCKER_IMAGE=your-dockerhub-id/hufs-notice-crawler:latest ``` - `APP_ENV`: 실행 환경 - `DB_USER`: PostgreSQL 사용자 -> 공통 .env에 있음 - `DB_PASSWORD`: PostgreSQL 비밀번호 -> 공통 .env에 있음 - `POSTGRES_HOST`: PostgreSQL 호스트 - `POSTGRES_PORT`: PostgreSQL 포트 - `POSTGRES_DB`: 데이터베이스 이름 - `BASE_URL`: 기본 크롤링 대상 사이트 - `REQUEST_TIMEOUT_SECONDS`: 외부 요청 타임아웃 - `MAX_PAGES_PER_BOARD`: 게시판별 최대 확인 페이지 수 - `DOCKER_IMAGE`: Docker Hub 이미지 이름 ## 주요 파일 - [`app/main.py`](/C:/Users/USER/Desktop/notice_crawler/app/main.py) - FastAPI 엔드포인트 - [`app/service.py`](/C:/Users/USER/Desktop/notice_crawler/app/service.py) - 크롤링 실행, bootstrap, DB 저장 로직 - [`app/crawler.py`](/C:/Users/USER/Desktop/notice_crawler/app/crawler.py) - 게시판 목록/상세 크롤러 - [`app/models.py`](/C:/Users/USER/Desktop/notice_crawler/app/models.py) - SQLAlchemy 모델 - [`sql/schema.sql`](/C:/Users/USER/Desktop/notice_crawler/sql/schema.sql) - PostgreSQL 스키마 - [`docker-compose.yml`](/C:/Users/USER/Desktop/notice_crawler/docker-compose.yml) - Docker Hub 이미지 pull 기반 실행 ## 참고 - 신규 여부 판단 기준은 게시판별 `article_id`입니다. - 반환 링크는 `artclView.do`가 아니라 실제 사용자용 `subview.do?enc=...` 형식입니다. - HTML 구조가 바뀌면 [`app/crawler.py`](/C:/Users/USER/Desktop/notice_crawler/app/crawler.py)의 selector 조정이 필요할 수 있습니다.