기술 기획서 - nhnacademy-aiot3-4iren/4iren-wiki GitHub Wiki
교육 시설(강의실·실습실)의 공기질(CO₂, 온도, 습도)과 냉난방·환기 설비는 대부분 사람이 감각적으로 판단해 수동으로 조작하거나, 아예 관리되지 않은 채 방치됩니다.
- 실내 공기질이 나빠져도 즉시 인지하기 어렵고, 대응이 항상 늦습니다.
- 공강 시간이나 방과 후에도 냉난방기가 계속 켜진 채 방치되어 에너지가 낭비됩니다.
- 환기를 해야 할지 냉방을 유지해야 할지 판단하려면 실내 상태뿐 아니라 바깥 날씨까지 종합해야 하는데, 매번 사람이 확인하고 판단하기 어렵습니다.
- 강의실마다 크기, 창문 유무, 단열 상태, 밀집도가 달라 일률적인 규칙(Rule-based) 제어로는 모든 공간에 최적화될 수 없습니다.
- 여러 강의실을 운영하는 교육기관은 공간마다 담당자가 따로 없어 관리 사각지대가 생깁니다.
본 솔루션은 교육 시설에 설치된 IoT 센서 데이터를 수집·관제하고, 규칙 기반 자동화와 LLM·ML 기반 지능형 판단을 결합해 사람이 매번 개입하지 않아도 실내 환경 상태를 파악하고 필요한 조치를 안내받을 수 있게 하는 AIoT 플랫폼입니다.
여러 강의실·실습실을 운영하며 공기질·에너지 관리를 체계화하고 싶은 교육기관(대학, 학원, 부트캠프 등).
- 비동기 파이프라인: 센서 패킷 인입부터 알림 발송까지 비동기 처리 (MQTT → RabbitMQ → 각 서비스). 실시간 스트리밍(SSE) API를 제공하며, 웹 화면은 조회 시점 기준 15분 평균을 표시
- 자동화의 2단계 구조: 사람이 정한 명시적 규칙(임계치 기반 플로우 엔진)과, 상황을 해석해 자연어로 설명·추천하는 LLM 판단을 함께 제공
- 맥락 있는 판단: 실내 센서 데이터뿐 아니라 기상청 실황·예보까지 결합해 "왜 이 조치가 필요한지"를 자연어로 설명 (웰컴 브리핑, 일일 요약)
- 피드백 학습: 텔레그램으로 모인 사용자 만족도 피드백을 야간 배치로 학습해, 공통 모델 위에 강의실별 보정값(residual adapter)과 선호 환경 프로필을 갱신
| 영역 | 요구사항 |
|---|---|
| 센서 수집 | 건물별 MQTT 브로커 동적 등록, LoRaWAN(ChirpStack) 업링크 수집 및 InfluxDB 적재 |
| 센서 관제 | 강의실별 센서 지표 조회 — 최신값 / 최근 15분 평균 / 임의 구간 시계열 |
| 센서 관제 | 센서 지표 실시간 스트리밍 API 제공 (SSE, Last-Event-ID 기반 재생) |
| 날씨 연동 | 기상청 초단기 실황·예보 조회, 강의실 위치 기반 지역 날씨 및 시간별 과거 날씨 재구성 |
| 웰컴 브리핑 | 강의실 상세 화면 진입 시, 밤새 축적된 환경 데이터 + 당일 날씨 + ML 추천을 종합한 브리핑 생성 (오전 10시 이전 한정) |
| 일일 요약 | 강의실별 실내 센서 데이터와 외부 날씨를 비교한 하루 요약 리포트 생성 |
| 브리핑 정책 | 팀·강의실별 브리핑 조건(강수확률·풍속·습도 임계값) 설정 |
| 피드백 학습 | 텔레그램 피드백을 야간 배치로 학습해 강의실별 보정값·선호 환경 프로필 갱신 후 모델 번들 배포 |
| 기기 운전 추천 | 학습된 ONNX 모델로 냉난방기 가동 시간대·환기 시점을 추천해 브리핑 근거로 제공 |
| 룰 엔진 | 강의실별 시각적 플로우(조건 노드 → 분기 → 알림 노드) 구성 및 활성/비활성 관리 |
| 룰 엔진 | 요일·시간대 스케줄로 플로우 동작 구간 제한, 센서 구성에 맞는 템플릿 추천 |
| 알림 | 관리자 대상 임계치 이탈 알림 (관리자 전용 봇, 발송 전용) |
| 알림 | 긴급 알림 즉시 발송 / 비긴급 알림 3분 디바운스 묶음 발송 |
| 자연어 질의응답 | "지금 환기해도 될까?" 등 도구 호출 기반 자연어 답변 (웹 챗봇 + 텔레그램 봇) |
| 피드백 수집 | 자유 텍스트에서 온도·습도·공기질 3축 감정 점수 추출, 환경 스냅샷과 결합해 학습 데이터로 적재 |
| 이력 조회 | 알림 발송 이력 조회(강의실·봇 종류·알림 유형·기간 필터), 기기 동작 이력 조회 |
| 조직 관리 | 팀 / 건물 / 강의실 / 기기 / 센서 CRUD, 초대 코드 기반 팀 가입 |
| 조직 관리 | 강의실 구독(셀프 등록) 및 구독별 알림 수신 여부 설정 |
| 조직 관리 | 역할(OWNER / ADMIN / NORMAL)별 접근 권한 차등 |
| 계정 | 로그인, JWT 기반 인증/인가, 비밀번호 초기화 메일 발송 |
| 결제 | 팀 Owner 등급 월간·연간 구독, 토스페이·카카오페이 빌링키 자동결제, 구독 취소 |
| 구분 | 요구사항 |
|---|---|
| 실시간성 | 텔레그램 웹훅 수신은 즉시 200 응답 후 RabbitMQ로 비동기 처리 (텔레그램 재전송 방지) |
| 장애 격리 | 알림 서비스는 Recommendation/Core 호출에 Resilience4j 서킷브레이커·폴백 적용, LLM 장애 시 안내 문구로 대체 응답 |
| 장애 격리 | 브리핑 생성 실패 시 강의실 상세 화면은 브리핑 영역 없이 정상 렌더링 |
| 서비스 독립성 | 서비스별 스키마 소유권 분리(타 서비스 소유 테이블 직접 접근·JOIN 금지, RabbitMQ 이벤트 및 openfeign을 활용하여 동기화) — 단, 물리 인스턴스·스키마(aiot03_team4)를 공유하며 서비스별 마이그레이션을 분리 |
| 확장성 | 센서·강의실 추가 시 파이프라인 코드 변경 없이 수집·검증·알림까지 동작 (단, ML 학습 대상 공간은 현재 코드에 정의된 목록에 한정) |
| 성능 | 센서 요약 조회는 Caffeine(L1) + Redis(L2) 계층 캐시, 브리핑·일일 요약·날씨는 Redis TTL 캐시 |
| 신뢰성 | 이상치 알림은 1시간 창 누적 횟수 기준 + 2시간 쿨다운, 룰 알림은 Redis 락 기반 중복 발송 차단 |
| 신뢰성 | 처리 실패 메시지는 DLQ로 격리 후 Elasticsearch에 원문·스택트레이스 보관 |
| 보안 | 텔레그램 봇 차단 시 알림 대상에서 자동 제외, 구독자 조회 시 비활성 계정 필터링 |
| 보안 | 게이트웨이가 주입한 X-USER-ID/X-USER-ROLE 헤더 기반 역할 검증, 본인 리소스만 조회·수정 허용 |
| 데이터 무결성 | 엔티티 간 참조를 명시적 FK 제약으로 선언 (@JoinColumn(foreignKey=...)) |
| 데이터 일관성 | 이벤트 기반 동기화 데이터는 최종적 일관성 전제, 역할 변경·관리자 생성 이벤트는 DLQ 재처리 경로 보유 |
| 오버엔지니어링 회피 | 실제 필요성이 확인되기 전까지 불필요한 스키마 확장·과도한 동기화 인프라 도입 지양 |
- 센서 수집·관제: MQTT 브로커 동적 등록, 최신값/15분 평균/구간 시계열 조회, SSE 실시간 스트리밍 API
- 지능형 웰컴 브리핑: 강의실 상세 진입 시 밤새 환경 데이터·당일 날씨·ML 추천을 종합한 브리핑 생성 (오전 10시 이전)
- 일일 환경 요약: 실내 센서와 외부 날씨를 비교한 하루 요약 리포트
- 피드백 학습 파이프라인: 텔레그램 피드백을 야간 배치로 학습해 강의실별 보정값·선호 프로필 갱신 후 모델 번들 배포
- 기기 운전 추천: 학습된 ONNX 모델 기반 냉난방기 가동 시간대·환기 시점 추천
- 룰 기반 자동화: 강의실별 시각적 플로우 구성, 요일·시간대 스케줄, 센서 구성 기반 템플릿 추천
- 텔레그램 알림: 관리자용(발송 전용)/사용자용(양방향 에이전트) 이원화 봇, 긴급 즉시 발송 + 비긴급 3분 묶음 발송
- 자연어 질의응답 및 피드백 루프: 도구 호출 기반 답변 + 3축 감정 점수 피드백 수집
- 이력 조회: 알림 발송 이력(다중 필터), 기기 동작 이력
- 조직 및 접근 권한 관리: 팀/건물/강의실/기기/센서 CRUD, 초대 코드 가입, 역할 기반 권한
- 구독 결제: 팀 Owner 등급 월간/연간 구독, 토스페이·카카오페이 빌링키 등록 및 자동결제

상세 버전 보기 아키텍처 보기 (모든 연결선 포함)

위 그림은 서비스 간 연결선을 전부 담은 버전이라 더 구체적이지만, 그만큼 선이 많아 한눈에 읽기는 어렵습니다. 평소엔 위쪽의 단순화 버전을 참고하고, 특정 연결 관계가 궁금할 때만 펼쳐보세요.
상세 버전 보기 아키텍처 보기 (모든 연결선 포함)

위 그림은 서비스 간 연결선을 전부 담은 버전이라 더 구체적이지만, 그만큼 선이 많아 한눈에 읽기는 어렵습니다. 평소엔 위쪽의 단순화 버전을 참고하고, 특정 연결 관계가 궁금할 때만 펼쳐보세요.
Nginx는 경로 두 개만 라우팅합니다. /telegram/**은 Front를 거치지 않고 Notification으로 직행(텔레그램 웹훅), 나머지 /**는 Front로 갑니다. Gateway는 브라우저가 직접 닿는 곳이 아니라 Front가 서버 사이드에서 호출하는 내부 홉이며, Auth·Account·Core·Recommendation·Payment·Processing으로 경로를 분배합니다. 나머지 서비스끼리는 Gateway를 거치지 않고 Eureka로 서로 직접 호출합니다.
그림에 등장하는 서비스는 AI Machine-Learning 한 곳을 빼면 전부 Spring Boot(Java 21) 입니다. AI Machine-Learning만 Python이며, 상시 컨테이너가 아니라 GitHub Actions가 매일 새벽 띄우는 배치 잡입니다 — 아래 머신러닝 파이프라인 참고.
웰컴 브리핑이 참조하는 ONNX 모델이 어떻게 갱신·배포되는지의 흐름입니다. 자세한 내용은 AI Learning 세부기능을 참고하세요.

- 사용자가 강의실 상세 화면에 들어가면 Recommendation이 브리핑을 생성하며, 이때 Core를 통해 센서 데이터와 기상청 실황·예보를 조회합니다.
- 브리핑에 쓰이는 ONNX 모델은 MinIO에서 내려받은 번들입니다.
- 이 번들은 AI Learning이 매일 03:17(KST) GitHub Actions로 실행하는 야간 배치가 갱신합니다. Notification을 통해 모인 사용자 피드백과 Core의 센서 시계열을 수집해, 검증을 통과한 번들만 MinIO에 게시합니다.
- 실시간 응답 경로(브리핑 생성)와 학습 경로(야간 배치)는 완전히 분리되어 있어, 학습이 지연되거나 실패해도 브리핑은 이전 번들로 계속 응답합니다.
Processing·Rule Engine이 RabbitMQ로 발행한 이벤트가 텔레그램에 닿기까지의 흐름입니다. 자세한 내용은 Notification API 세부기능을 참고하세요.

- 긴급(임계치 이상탐지·룰 알림)은 즉시 개별 발송되며 관리자 봇에만 갑니다.
- 비긴급은 사용자별로 3분 동안 Redis에 모았다가 강의실 단위로 묶어 관리자·사용자 봇 모두에 다이제스트로 보냅니다.
- 두 경로 모두 발송 직전 3중 게이트(구독의 알림 수신 설정 · 봇 연동 및 비차단 · 동일 이벤트 중복 아님)를 통과해야 합니다.
- 메시지 소비에 실패하면 DLQ로 격리되어 Elasticsearch에 원문과 스택트레이스가 보관됩니다. 이 실패 로그는 Grafana Alert Rule이 감지해 운영팀 전용 텔레그램 채널(제품 알림과는 다른 별도 봇)로 통보합니다 — 즉 알림 발송 실패 자체가 하나의 관측성(observability) 알림으로 이어집니다.
센서 메시지가 강의실별 자동화 플로우를 만나 알림으로 이어지기까지의 흐름입니다. 자세한 내용은 Rule Engine 세부기능을 참고하세요.

- RabbitMQ로 수신한 센서 데이터가 트리거이며, 별도의 주기 폴링·스케줄 평가는 없습니다.
- 활성 플로우는 Redis 캐시로 조회하고, 요일·시간대 스케줄을 통과한 플로우만 실행됩니다.
- 그래프 순회는 START의 참(TRUE) 분기부터 시작하는 BFS이며, 조건 노드 중 실제로 발동하는 것은 THRESHOLD뿐입니다(AVERAGE/DURATION/GRADIENT는 현재 미동작).
- ALERT 노드는 순회 도중 인라인으로 실행되며, 노드 ID+판정 결과 해시를 키로 Redis 락을 걸어 쿨다운 시간 동안 중복 발행을 막습니다.
- 발행된 이벤트는 RabbitMQ를 거쳐 Notification 서비스가 실제 텔레그램 발송을 수행합니다.
강의실 대시보드가 센서 요약을 어떤 순서로 조회하는지의 흐름입니다. 자세한 내용은 Core 세부기능을 참고하세요.

- 공간별 센서 요약(최근 15분 평균)·최신값 조회는 Caffeine(L1) → Redis(L2) → InfluxDB(원본) 순서의 계층 캐시로 처리합니다.
- 두 캐시 모두 미스일 때만 InfluxDB를 직접 조회하며, 조회 결과는 응답 전에 L1·L2 캐시에 다시 적재되어 다음 요청의 부하를 줄입니다.
강의실 상세 화면 진입 시 브리핑이 어떻게 합성되는지의 흐름입니다. 자세한 내용은 Recommendation 세부기능을 참고하세요.

- 요청받은 강의실 1곳에 한해 생성하는 요청 시 생성(pull) 방식이며, 오전 10시 이후 요청은 거부됩니다(구독 강의실 전체를 순회하거나 이벤트로 자동 발송하지 않음).
- 현재 센서·날씨(Core API 경유)와 학습된 ONNX 모델의 기기 운전 추천을 병렬로 조회한 뒤, Gemini가 두 근거를 종합해 자연어 브리핑을 생성합니다.
- 결과는 팀·강의실·날짜 단위로 Redis에 1시간 캐시되며, 이 서비스 자체엔 서킷브레이커가 없어 실패 시 웹은 브리핑 없이 화면만 렌더링합니다.
| 서비스 | 역할 |
|---|---|
| Auth API | JWT 인증/인가 |
| Account API | 사용자/팀/강의실 정보 관리 |
| Core API | 센서/날씨 데이터 제공, 환경 지표 계산 |
| Recommendation API | LLM 기반 자연어 질의응답, 추천, 브리핑 |
| processing | MQTT 센서 데이터 수집, 검증/정제, InfluxDB 적재 |
| Rule Engine | 임계치/복합조건 룰 판단, 이상탐지, 이벤트 발행 |
| AI Learning | 피드백/센서 이력 기반 야간 모델 갱신 배치, ONNX 번들 MinIO 배포 |
| Notification API | 텔레그램 봇 연동, 알림 발송, 피드백 수집 |
| Payment API | 구독 결제(토스·카카오페이), 등급 승격/강등 이벤트 발행 |
⚠️ AI Predict API는 별도 서비스로 개발되다 중단되어 저장소가 삭제되었습니다. 모델 서빙은 Recommendation API가 ONNX Runtime으로 직접 수행합니다.
- Backend: Spring Boot 3.x, Java 21 / Python 3.12 (ML 배치)
-
DB: MySQL (prod는 공유 스키마
aiot03_team4, 서비스 간 논리적 분리), InfluxDB (시계열), Elasticsearch (이상 로그·DLQ 아카이빙) - Cache: Redis
- Messaging: RabbitMQ, MQTT
- AI/ML: Spring AI + Google Gemini, ONNX Runtime, scikit-learn, MinIO (모델 번들 저장소)
- Infra: Eureka(Service Discovery), Gateway, Config Server, Docker, GitHub Actions
이슈 기반 워크플로우(Issue → Feature 브랜치 → PR 자동 연결), develop 병합 전 최소 승인+테스트 통과를 강제하는 코드 리뷰, RabbitMQ 등 핵심 기술을 팀 표준으로 맞추는 기술 공유 세미나(총 9회)를 기반으로 협업합니다.
| 브랜치 | 용도 | 명명 규칙 |
|---|---|---|
main |
제품으로 배포되는 최종 브랜치 | - |
develop |
다음 릴리스를 준비하는 핵심 개발 브랜치 | - |
feature/ |
신규 기능 개발 |
feature/기능-설명 또는 feature/#이슈번호-기능 (예: feature/#23-login) |
fix/ (bugfix/) |
일반 버그 수정 |
fix/버그-설명 (예: fix/#12-auth-error) |
release/ |
배포 전 최종 테스트·준비 | - |
hotfix/ |
운영 중 긴급 수정 | - |
핵심 규칙: 기능 개발·버그 수정은 반드시 feature/ 또는 fix/ 브랜치에서 작업한 뒤, develop으로 PR을 생성해 병합합니다.
-
이슈 타입(단일 분류):
Task(특정 작업) /Bug(예상치 못한 문제·동작) /Feature(요청·아이디어·신규 기능) -
레이블(다중 태그):
bug,documentation,enhancement,good first issue,help wanted,question,duplicate,invalid,wontfix
제목 맨 앞에 대괄호로 타입을 명시하고([Bug], [Feature], [Task], [Docs], [Refactor], [Fix]), 불필요한 수식어 없이 동사형/명령문으로 50자 이내에 작성합니다. 등록 전 중복 이슈·PR 검색은 필수입니다.
- 이슈 예:
[Bug] 로그인 시 500 에러 발생,[Feature] 다크 모드 지원 추가 - PR 예:
[Feature] 데이터 검색 필터 기능 추가 (#45),[Fix] 회원가입 시 아이디 중복 체크 오류 수정 (#12)
- 칸반보드(GitHub Projects): TODO → In progress → Blocked → Done 4단계로 이슈를 트래킹
- 회의록: Notion에서 작성 후 "완료" 체크 시 GitHub 이슈·칸반보드·위키 회의록 페이지까지 자동 반영 (Daily/Team/Weekly)
자세한 원문은 GitFlow 브랜치 전략 · 커밋 메시지 규칙 · Label과 이슈타입 안내 · 이슈/PR 명명규칙을 참고하세요.