중앙대학교 캡스톤디자인(1) 팀 프로젝트
초등학생을 위한 IoT & AI 기반 스마트 반려 식물 교육 플랫폼 아날로그 방식의 식물 키우기를 넘어, 데이터를 통해 식물과 교감하고 과학적 사고력을 기르는 에듀테크(Edu-Tech) 어플리케이션입니다.
| OnBoarding | Home | Hospital |
|---|---|---|
![]() |
![]() |
![]() |
기존의 식물 키우기 활동은 초등학생들에게 자칫 지루하고 반복적인 과정으로 느껴질 수 있습니다.
쑥자씨는 이러한 아날로그 방식을 디지털로 전환하여, 아이들이 스스로 식물의 상태를 파악하고 과학적 원리를 체험하며 즐겁게 식물을 키울 수 있도록 돕습니다. 아두이노 센서 데이터와 AI 기술을 활용하여 식물의 생장 과정을 논리적으로 이해하고, 자연과학적 사고력을 함양하는 것을 목표로 합니다.
아두이노 센서를 통해 식물의 온도, 습도, 토양 수분 데이터를 실시간으로 수집합니다.
- 눈높이 상태 알림: 딱딱한 수치 대신 "목이 말라요", "너무 추워요" 등 초등학생이 직관적으로 이해할 수 있는 캐릭터(쑥자씨)의 대사로 변환하여 알려줍니다.
- 스마트 물주기 가이드: 토양 수분 센서 데이터를 바탕으로 사용자가 식물에게 물을 올바르게 주고 있는지 파악하고 피드백을 제공합니다.
식물에 이상이 생겼을 때, 사진 한 장으로 질병을 진단하고 원인을 과학적으로 분석합니다.
- AI 질병 진단: 촬영된 식물 사진을 AI가 분석하여 질병명, 주요 증상, 예방법 및 맞춤형 치료법을 제시합니다.
- 데이터 기반 원인 규명: 단순히 병명만 알려주는 것이 아니라, 지금까지 기록된 온도/습도 데이터와 질병의 인과관계를 설명해줌으로써 아이들이 환경과 식물 건강의 상관관계를 과학적으로 이해하도록 돕습니다.
지속적인 학습과 관리에 대한 동기를 부여하기 위해 흥미로운 보상 시스템을 도입했습니다.
- 보상 시스템: 앱 접속(출석), 올바른 물주기, 병든 식물 치료 등 긍정적인 행동을 할 때마다 코인을 획득합니다.
- 나만의 도감 채우기: 획득한 코인으로 '식물 조각 뽑기'를 진행하여 다양한 식물 조각을 모으고, 나만의 도감을 완성하며 성취감을 느낄 수 있습니다.
- Python 3.11
- FastAPI
- Pydantic
- SQLAlchemy 2.x (Async)
- Alembic
- PostgreSQL
- asyncpg
- Redis 7
- arq
- Docker / Docker Compose
- FastAPI API Container
- arq Worker Container
- Redis Queue
- AWS EC2
- Amazon S3
- GitHub Container Registry (GHCR)
- GitHub Actions
- 과학적 사고력 증진: 데이터를 기반으로 식물의 상태 변화 원인을 추론하며 논리적 사고력을 기릅니다.
- 자기 주도적 학습: AI의 가이드에 따라 스스로 식물을 관찰하고 돌보는 과정을 통해 문제 해결 능력을 키웁니다.
- 정서 함양: 디지털 펫(캐릭터)과의 상호작용을 통해 생명에 대한 애정과 책임감을 배웁니다.
- Docker Compose v2가 설치되어 있어야 합니다.
- 프로젝트 루트에 학습된 DSPy 모델 디렉터리인
compiled_leaf_disease/가 있어야 합니다. .env.example을 복사하여.env파일을 만들고OPENAI_API_KEY, PostgreSQL, Redis 연결 정보를 설정합니다.
.env에는 API 키와 같은 민감한 값이 포함되므로 Docker 이미지에 포함하거나 Git에 커밋하지 마세요.
이미지를 빌드하고 컨테이너를 백그라운드에서 실행합니다.
docker compose up --build -d실행 후 http://localhost:8000/docs에서 API 문서를 확인할 수 있습니다.
로그 확인:
docker compose logs -f ai컨테이너 중지 및 제거:
docker compose down코드나 의존성이 변경된 경우 캐시 없이 다시 빌드할 수 있습니다.
docker compose build --no-cache
docker compose up -d호스트의 8000 포트가 이미 사용 중이라면 compose.yaml의 ports에서 왼쪽 호스트 포트를 다른 값으로 변경하세요. 예를 들어 8001:8000으로 변경하면 http://localhost:8001로 접속할 수 있습니다.
Docker Compose 없이 실행하려면 다음 명령을 사용합니다.
이 경우 컨테이너에서 접근할 수 있는 PostgreSQL과 Redis가 별도로 실행 중이어야 합니다.
docker build -t sjseed-ai .
docker run --rm --env-file .env -p 8000:8000 sjseed-aiAWS와 GitHub Actions를 기반으로 Docker 이미지 빌드부터 EC2 배포까지 자동화된 배포 환경을 구성했습니다.
-
Amazon EC2
- Ubuntu 기반 애플리케이션 서버
- Docker / Docker Compose를 이용한 컨테이너 실행
- Elastic IP를 이용한 고정 IP 구성
-
Amazon S3
- 학습된 DSPy 모델(
compiled_leaf_disease) 저장 - Git에 포함하지 않는 모델 아티팩트 관리
- 학습된 DSPy 모델(
-
AWS IAM
- GitHub Actions OIDC 인증을 통한 S3 접근 권한 관리
- 장기 AWS Access Key 없이 모델 다운로드
-
GitHub Container Registry (GHCR)
- 빌드된 FastAPI Docker 이미지 저장
latest및 Commit SHA 기반 이미지 태그 관리
-
GitHub Actions
main브랜치 반영 시 CD Workflow 실행- S3에서 모델 다운로드
- Docker 이미지 빌드 및 GHCR Push
- EC2에 배포 후 Health Check 수행
Amazon S3
compiled_leaf_disease
│
│ OIDC / IAM
▼
GitHub Actions
│
Docker Image Build
│
▼
GHCR
│
Docker Pull
│
▼
+--------------------------------------------------+
| Amazon EC2 |
|--------------------------------------------------|
| Ubuntu |
| Docker / Docker Compose |
| FastAPI Container |
| arq Worker Container |
| PostgreSQL |
| Redis Queue |
+--------------------------------------------------+
│
▼
GET /health
main 브랜치에 변경사항이 반영되면 다음 과정으로 자동 배포됩니다.
main Push / Merge
↓
S3 모델 다운로드
↓
Docker 이미지 빌드
↓
GHCR Push
↓
EC2 SSH 접속
↓
최신 이미지 Pull
↓
Docker Compose 재배포
↓
Health Check
배포된 애플리케이션의 상태는 /health 엔드포인트를 통해 확인합니다.
상태 확인 엔드포인트는 용도에 따라 다음과 같이 구분합니다.
| 엔드포인트 | 확인 범위 | 정상 응답 | 장애 응답 |
|---|---|---|---|
/health/live |
FastAPI 프로세스 | 200 |
프로세스가 응답하지 않음 |
/health, /health/ready |
API, PostgreSQL, Redis | 모두 정상이면 200 |
의존 서비스 하나라도 비정상이면 503 |
curl -i http://localhost:8000/health/live
curl -i http://localhost:8000/healthReadiness 응답은 서비스를 구분해 반환합니다.
{
"status": "healthy",
"services": {
"api": {"status": "up"},
"postgresql": {"status": "up"},
"redis": {"status": "up"}
}
}모니터링 구성은 deploy/monitoring에 있으며 다음 순서로 한 번 적용합니다. AWS 설정 스크립트를 다시 실행하면 기존 SNS topic, IAM role과 Alarm을 갱신해 재사용합니다.
사전 조건:
- 로컬 실행 환경에 AWS CLI v2가 설치되고 대상 계정으로 인증되어 있어야 합니다.
- 실행 주체에는 EC2 조회·instance profile 연결, IAM role/policy, CloudWatch, CloudWatch Logs, SNS 설정 권한이 필요합니다.
- 대상 EC2에 SSH와
sudo로 접근할 수 있어야 합니다.
먼저 AWS 리소스를 구성합니다. instance ID와 이메일은 예시 값으로 바꾸며 저장소 파일에 기록하지 않습니다.
AWS_REGION=ap-northeast-2 bash deploy/monitoring/provision-aws.sh \
--instance-id i-0123456789abcdef0 \
--email operator@example.com스크립트는 기존 EC2 instance profile을 재사용하거나 전용 profile을 연결하고, CloudWatch Agent 권한, SNS topic, 30일 보존 로그 그룹과 Alarm을 구성합니다. 새 SNS 이메일 구독은 AWS가 보낸 메일의 Confirm subscription 링크를 눌러야 알림을 받을 수 있습니다.
그다음 EC2의 최신 저장소에서 Agent와 1분 주기 health probe를 설치합니다.
cd ~/opt/AI
sudo bash deploy/monitoring/install-on-ec2.shAgent 설정을 변경한 경우에도 같은 설치 명령을 다시 실행하면 새 설정이 반영됩니다.
CloudWatch Agent 지표와 서비스 상태 지표는 SJSeed/AI, EC2 기본 지표는 AWS/EC2 namespace에서 확인합니다.
| Alarm | 조건 | 누락 데이터 처리 |
|---|---|---|
| CPU | 5분 평균 CPUUtilization >= 80% |
장애 |
| 메모리 | 5분 평균 mem_used_percent >= 80% |
장애 |
| 루트 디스크 | 5분 평균 disk_used_percent >= 80% |
장애 |
| EC2 instance/system 상태 검사 | 1분 간격 2회 연속 실패 | 유지 |
| API | HealthApi == 0 2회 연속 |
장애 |
| PostgreSQL | HealthPostgresql == 0 2회 연속 |
정상 |
| Redis | HealthRedis == 0 2회 연속 |
정상 |
API에 연결하지 못하면 PostgreSQL과 Redis 상태를 추측하지 않고 해당 지표를 생략합니다. 따라서 API Alarm이 probe나 Agent 자체의 데이터 누락까지 감지하고, 의존 서비스 Alarm은 /health가 명시적으로 down을 반환할 때만 발생합니다. 모든 Alarm은 ALARM과 OK 전환을 같은 SNS topic으로 알립니다.
EC2에서 Agent와 timer 상태를 확인합니다.
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status
systemctl status sjseed-health-probe.timer
systemctl list-timers sjseed-health-probe.timer
journalctl -u sjseed-health-probe.service --since "10 minutes ago"API와 worker의 Docker 로그는 EC2의 /var/log/sjseed/application.log에 스트리밍된 뒤 /sjseed/ai/docker CloudWatch Logs group에 30일간 저장됩니다. 로그 스트리밍 서비스는 설치 이후 새로 발생한 항목만 전달하며, 호스트 파일은 매일 순환해 7개를 보관합니다. 분석 상태 로그는 CloudWatch Logs Insights에서 다음처럼 찾을 수 있습니다.
fields @timestamp, @message
| filter @message like /analysis_status_changed/
| sort @timestamp desc
| limit 100
이 로그에는 analysis_id, status, duration_ms, retry_count, failure_reason이 포함되며 원본 예외, API key, DB/Redis 접속 URL은 기록하지 않습니다.
SNS 전달 경로는 서비스를 중단하지 않고 다음 명령으로 확인할 수 있습니다. Alarm 이름은 대상 instance ID에 맞게 변경합니다.
aws cloudwatch set-alarm-state \
--region ap-northeast-2 \
--alarm-name sjseed-ai-i-0123456789abcdef0-health-api-down \
--state-value ALARM \
--state-reason "monitoring verification"
aws cloudwatch set-alarm-state \
--region ap-northeast-2 \
--alarm-name sjseed-ai-i-0123456789abcdef0-health-api-down \
--state-value OK \
--state-reason "monitoring verification complete"실제 의존 서비스 장애 검증은 운영 트래픽이 없는 점검 시간에만 수행합니다. Redis 또는 PostgreSQL 컨테이너를 하나씩 중단하고 /health의 503 및 해당 서비스 down, 2분 뒤 Alarm과 이메일을 확인한 다음 즉시 컨테이너를 복구하고 /health와 OK 알림을 다시 확인합니다. 두 의존 서비스를 동시에 중단하거나 데이터 volume을 삭제하지 않습니다.
AI 분석은 HTTP 요청에서 직접 실행하지 않고 Redis Queue와 별도 Worker를 통해 비동기로 처리합니다.
- FastAPI가 분석 요청을 DB에
PENDING상태로 저장합니다. - Redis Queue에
analysis_id를 등록합니다. - API는 분석 완료를 기다리지 않고
202 Accepted를 반환합니다. - Worker가 작업을 가져와
PROCESSING상태로 변경합니다. - 이미지 다운로드와 AI 분석을 수행합니다.
- 성공하면
COMPLETED, 재시도할 수 있는 오류가 발생하면PENDING상태로 되돌린 뒤 다시 처리합니다. - 재시도할 수 없는 오류가 발생하거나 최대 재시도 횟수를 모두 소진하면
FAILED상태로 저장합니다.
Client
│ POST /analyze
▼
FastAPI ── 작업 생성 ──▶ PostgreSQL
│
├── 202 Accepted ──▶ Client
│
└── analysis_id ──▶ Redis Queue
│
▼
arq Worker
│
이미지 다운로드·AI 분석
│
▼
PostgreSQL


