대신물류 배차현황 데이터를 크롤링하여 카카오톡 챗봇으로 조회할 수 있는 서비스입니다.
- 자동 크롤링: 월~토 오전 6시, 오후 2시 배차현황 자동 수집
- 데이터 저장: SQLite DB에 저장 및 이력 관리
- 카카오톡 챗봇: 노선코드, 차량번호, 도착지 검색
- REST API: 외부 시스템 연동용 API 제공
| 영역 | 기술 |
|---|---|
| 런타임 | Node.js 20+ (ESM) |
| 언어 | TypeScript |
| 웹 프레임워크 | Express 5 |
| ORM | Prisma (SQLite) |
| DI 컨테이너 | TSyringe |
| 크롤링 | Cheerio + Axios |
| 스케줄링 | node-cron |
| 컨테이너 | Docker |
Clean Architecture + Dependency Injection 패턴을 따릅니다.
src/
├── domain/ # 핵심 비즈니스 로직 (외부 의존성 없음)
│ ├── entities/ # Route 엔티티
│ ├── repositories/ # IRouteRepository 인터페이스
│ ├── ports/ # ICrawler 인터페이스
│ └── value-objects/# LineCode, SearchDate 값 객체
├── application/ # 유스케이스 (비즈니스 로직 조율)
│ ├── use-cases/ # SearchByCode, SearchByName, SyncRoutes 등
│ └── dto/ # 데이터 전송 객체
├── infrastructure/ # 외부 구현체
│ ├── persistence/ # PrismaRouteRepository
│ └── crawling/ # CheerioHttpCrawler
├── interface/ # 진입점
│ ├── http/ # REST API 컨트롤러
│ └── kakao/ # 카카오 스킬 서버
└── config/ # DI 컨테이너, 환경설정
- Node.js 20+
- npm 또는 yarn
- Docker (선택)
# 의존성 설치
npm install
# 환경 변수 설정
cp .env.example .env
# 데이터베이스 초기화
npx prisma db push
# 개발 서버 실행
npm run dev# 데이터 디렉토리 생성
mkdir -p data
# 빌드 및 실행
docker compose up -d
# 로그 확인
docker logs -f daesin-logistics-bot| 변수 | 설명 | 기본값 |
|---|---|---|
PORT |
서버 포트 | 3000 |
NODE_ENV |
실행 환경 | development |
DATABASE_URL |
SQLite DB 경로 | file:/app/data/logistics.db |
- 로컬:
./logistics.db - Docker:
./data/logistics.db(볼륨 마운트)
Docker 볼륨으로 호스트에 마운트되어 컨테이너 재시작 시에도 데이터가 유지됩니다.
model Route {
id Int @id @default(autoincrement())
searchDate String // 검색 날짜 (YYYYMMDD)
lineCode String // 노선 코드 (6자리)
lineName String? // 노선명 (출발→도착)
carCode String? // 차량 코드
carNumber String? // 차량 번호
count Int? // 건수
quantity Int? // 수량
sectionFare Float? // 구간 운임
totalFare Float? // 총 운임
createdAt String? // 생성 시각
@@unique([searchDate, lineCode])
}# 스키마 변경 후 적용
npx prisma db push
# Prisma 클라이언트 재생성
npx prisma generatenode-cron을 사용하여 서버 프로세스 내부에서 스케줄링됩니다.
| 스케줄 | 시간 | 설명 |
|---|---|---|
| 월~토 | 오전 6시 ~ 오후 8시 | 매시 정각 크롤링 (1시간 간격) |
| 서버 시작 | 즉시 | 초기 동기화 1회 실행 |
// cron 표현식: 0 6-20 * * 1-6
cron.schedule('0 6-20 * * 1-6', async () => {
await syncUseCase.execute();
});컨테이너가 재시작되어도 스케줄은 자동으로 다시 등록됩니다.
GET /health
{"status": "ok", "timestamp": "2026-01-24T12:30:00.000Z"}GET /api/routes/code/:code # 노선코드로 검색
GET /api/routes/name/:name # 노선명으로 검색
GET /api/routes/car/:number # 차량번호로 검색
GET /api/routes/date/:date # 날짜별 검색 (YYYYMMDD)
GET /api/stats/:date
{
"totalRoutes": 729,
"totalCount": 110283,
"totalQuantity": 161219,
"totalSectionFare": 664355601.2,
"totalFare": 769223956
}POST /api/sync
Content-Type: application/json
{"date": "20260124"}
POST /kakao/skill
| 명령어 | 설명 | 예시 |
|---|---|---|
노선 {코드} |
노선코드로 검색 | 노선 101102 |
차량 {번호} |
차량번호로 검색 | 차량 4536 |
도착 {지역} |
노선명/도착지로 검색 | 도착 연희동 |
오늘 현황 |
오늘 전체 현황 | 오늘 현황 |
어제 현황 |
어제 전체 현황 | 어제 현황 |
도움말 |
사용법 보기 | 도움말 |
- 카카오 비즈니스 가입 및 채널 생성
- 카카오 i 오픈빌더 접속
- 봇 생성 후 스킬 등록
- 스킬 URL:
https://your-domain.com/kakao/skill
- 스킬 URL:
- 시나리오 블록에 스킬 연결
Traefik 리버스 프록시를 사용한 Blue-Green 배포 구조:
Internet
↓
Tailscale Funnel (HTTPS, *.ts.net)
↓
localhost:80
↓
Traefik (리버스 프록시)
├── app-blue (활성) ─┐
└── app-green (대기) ─┴── SQLite DB (./data)
| 컴포넌트 | 역할 | 포트 |
|---|---|---|
| Traefik | 리버스 프록시, 라우팅, 헬스체크 | 80 (웹), 8080 (대시보드) |
| app-blue | 프로덕션 서비스 (Blue) | 3000 (내부) |
| app-green | 프로덕션 서비스 (Green) | 3000 (내부) |
# 시작 (Blue 활성, 기본값)
docker compose up -d
# 중지
docker compose down
# 로그 확인
docker logs -f app-blue
docker logs -f traefik
# Traefik 대시보드
open http://localhost:8080/dashboard/Blue-Green 배포 전략으로 무중단 배포를 지원합니다.
# git pull + build + Blue-Green 전환
./scripts/deploy.sh스크립트가 자동으로:
- 최신 코드 pull
- 새 이미지 빌드
- 비활성 서비스(Blue/Green) 시작
- 헬스체크 대기
- 트래픽 전환
- 이전 서비스 정리
# 현재 상태 확인
docker compose ps
# Blue → Green 전환
BLUE_ENABLED=false GREEN_ENABLED=true docker compose up -d
# Green → Blue 롤백
BLUE_ENABLED=true GREEN_ENABLED=false docker compose up -d| 변수 | 기본값 | 설명 |
|---|---|---|
BLUE_ENABLED |
true |
Blue 서비스 활성화 |
GREEN_ENABLED |
false |
Green 서비스 활성화 |
Traefik 도입으로 Funnel 포트가 변경됩니다:
# 기존 포트 3000 → 새 포트 80
tailscale funnel --bg localhost:80
# 기존 funnel 제거 (필요시)
tailscale funnel off localhost:3000# DB 파일 백업
cp ./data/logistics.db ./backup/logistics_$(date +%Y%m%d).dbnpm run dev # 개발 서버 (hot reload)
npm run build # TypeScript 빌드
npm start # 프로덕션 서버
npm test # 테스트 실행
npm run test:watch # 테스트 감시 모드
npm run sync # 수동 데이터 동기화# 전체 테스트
npm test
# 특정 파일
npx vitest run tests/api.test.ts
# 특정 테스트
npx vitest run -t "헬스체크"ISC