๋ณธ ํ๋ก์ ํธ๋ ์ด๊ฒฝ๋/์ด์ ๊ฐ ์ธํ๋ผ ํ๊ฒฝ์์ ๋์ ์์คํ ์์ ์ฑ์ ํ๋ณดํ๊ธฐ ์ํด ์๋์ ๊ฐ์ 3๋ ์์ง๋์ด๋ง ์ ์ฝ ์กฐ๊ฑด ๋ฐ ๊ทน๋ณต ์์น์ ์๋ฆฝํ์ฌ ์ค๊ณ๋์์ต๋๋ค.
rogic.io๋ ์ ํต์ ์ธ ์ฌ๊ฐํ ๊ฒฉ์ํ์์ ํผ์ฆ์ ํด๊ฒฐํ๋ ๋ค๋ชจ๋ก์ง(๋
ธ๋
ธ๊ทธ๋จ) ๊ฒ์์
๋๋ค. ๋จ, ์ถ์ ์์ ์ ์์์ ๊ฐ๋๋ก ํ์ ๋ ํผ์ฆ์ ํด๊ฒฐํ๋ฉด, ์๋ฃ๋๋ ์๊ฐ ์๋ ๋ฐฉํฅ์ผ๋ก ์๋ ํ์ ๋ณต์๋๋ฉฐ ์์ฑ๋ ํจํด์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ณด์ฌ์ฃผ๋ ๋ฉ์ปค๋์ฆ์ ๋ด์ฅํ๊ณ ์์ต๋๋ค.
| Service Environment | Live URL | Deployment Status |
|---|---|---|
| ๐ Production | rogic.io | |
| ๐งช Staging | stage.rogic.io |
| Category | Technologies |
|---|---|
| Frontend | |
| Backend | |
| Database | |
| Infra & IaC | |
| CI/CD | |
| Telemetry |
C4Context
title System Context Diagram for rogic.io (Level 1: System Context)
Person(player, "Player / User", "Accesses the puzzle game through a web browser.")
System_Boundary(dns_cdn, "Global Edge Delivery") {
System_Ext(route53, "Route 53", "DNS management mapping domains to CloudFront & EC2.")
System_Ext(cloudfront, "Amazon CloudFront", "CDN distributing static web assets globally.")
System(s3, "Amazon S3 Bucket", "Stores Vite-built Vue static compilation files.")
}
System_Boundary(backend, "Core API Server") {
System(api, "rogic.io REST API (EC2)", "Spring Boot backend handling gameplay, XP levels, and leadership stats.")
SystemDb(postgres, "PostgreSQL DB", "Relational database storing user logs, statistics, and stage metadata.")
}
Rel(player, route53, "Queries DNS for rogic.io / api.rogic.io", "DNS Protocol")
Rel(player, cloudfront, "Requests static assets", "HTTPS / Port 443")
Rel(cloudfront, s3, "Pulls origin static files", "S3 Protocol")
Rel(player, api, "Calls REST API services (via DNS mapped to EC2 EIP)", "HTTPS / Port 443")
Rel(api, postgres, "Reads/Writes game state", "JDBC & JPA / Port 5432")
- Global Edge Delivery (Route 53 / CloudFront / S3)
Vite ์ปดํ์ผ ๊ฒฐ๊ณผ๋ฌผ์Amazon S3๋ฒํท(OAC ์ค์ ์ ํตํ ์ ๋ฉด ์ฐจ๋จ)์ ๋ฐฐํฌํ๊ณ ,Amazon CloudFrontCDN์ ํตํด ๊ธ๋ก๋ฒ ์ฃ์ง์ ์บ์ฑ ๋ฐฐํฌํ์ฌ ์ง์ฐ ์๊ฐ์ ์ต์ํํ๊ณ S3 ์ง์ ์์ฒญ ์๊ธ์ ์ฐจ๋จํ์ต๋๋ค. - Core API Server & Database (EC2 / PostgreSQL)
๋จ์ผ EC2 ์ธ์คํด์ค ๋ด์์ SSL/TLS ์ข ๋จ ๋ฐ ํฌํธ ํฌ์๋ฉ์ ์ํํ๋ Nginx ํ๋ก์, REST API๋ฅผ ์ฒ๋ฆฌํ๋ Spring Boot ์ปจํ ์ด๋, ๊ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ํํ๋ PostgreSQL DB ์ปจํ ์ด๋๋ฅผ ๊ฐ์ Docker ๋ธ๋ฆฟ์ง ๋คํธ์ํฌ๋ก ๋ถ๋ฆฌ ๊ฐ๋ํฉ๋๋ค.
- ์ต์ ํ ์กฐ์น (Optimization)
- ์ $3.5 ๋์ ์ด๊ฒฝ๋ t3a.nano ์ธ์คํด์ค(512MB RAM) ํ๊ฒฝ ๋์
- Spring Boot ๋ฐํ์ ๋ฉ๋ชจ๋ฆฌ ํํ๋ฆฐํธ๋ฅผ 30MB ์ดํ๋ก ๋ฎ์ถ๊ธฐ ์ํด GraalVM Native Image ๋น๋ ๋์
- Jackson ์ญ์ง๋ ฌํ ์ค๋ฅ ์๋ฐฉ์ ์ํด NemologicRuntimeHints.java์ ๋ฆฌํ๋ ์ ํํธ ๋ช ์
- ํธ์คํธ ๋์คํฌ ์ฉ๋ ๊ด๋ฆฌ๋ฅผ ์ํด ๋งค์ผ ์๋ฒฝ 3์๋ง๋ค Docker GC prune ์ค์ผ์ค ํฌ๋ก ํญ ๊ตฌ๋
- ๊ธฐ์ ์ ์ ์ฝ (Trade-off)
- 512MB ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ์ผ๋ก ์ธํด ์๋ฒ ๋ด์์ ์ง์ GraalVM ์ปดํ์ผ ๋น๋๊ฐ ๋ถ๊ฐ๋ฅํ๋ฉฐ, ๋น๋ ์ฐ์ฐ ์ JVM ์ปดํ์ผ ๋๋น 10๋ฐฐ ์ด์์ ์๊ฐ ์์
- ์ํ ๋์ฑ
(Mitigation)
- CI/CD ํ์ดํ๋ผ์ธ ์์์ GitHub Actions๊ฐ ์ ๊ณตํ๋ ์ธ๋ถ ๋น๋ ์ธํ๋ผ(2 Core, 7GB RAM)์ ์ปดํ์ผ ์ฐ์ฐ ๋ถํ๋ฅผ ์คํ๋ก๋ฉํ๊ณ , ํธ์คํธ ์๋ฒ๋ 30MB ์์ค์ ๋ฌด๋ถํ ๋ฐ์ด๋๋ฆฌ ์คํ๋ง ์ ๋ดํ๋๋ก ๋ถ๋ฆฌ
- ์ต์ ํ ์กฐ์น (Optimization)
- ์ $20 ์๋น์ AWS ALB(Application Load Balancer)๋ฅผ ๋ฐฐ์ ํ๊ณ Route 53 ๋๋ฉ์ธ๊ณผ ๊ณ ์ Elastic IP ๋ค์ด๋ ํธ ๋งคํ
- Docker Nginx ์ปจํ ์ด๋ ๋จ์ผ ํ๋ก์ ๊ฐ๋์ ํตํ SSL/TLS ์ข ๋จ ๋ฐ ๋ฐฑ์๋ API ํฌํธ(8080) ํฌ์๋ฉ ์ฒ๋ฆฌ ์ ๋ด
- Vite ๋น๋ ์ ์ ์ปดํ์ผ ์์ฐ์ S3 ๋ฒํท์ OAC(Origin Access Control) ๋ณด์ ์ค์ ์ผ๋ก ๋ฐฐํฌํ๊ณ CloudFront CDN์ ์ฐ๋ํด ๊ธ๋ก๋ฒ ์์ง ์บ์ฑ ์ ์ก์ ๊ตฌํํ์ฌ ์ง์ฐ ์๊ฐ ๋จ์ถ ๋ฐ S3 ์ง์ ์์ฒญ ์๊ธ ์ฐจ๋จ
- ๊ธฐ์ ์ ์ ์ฝ (Trade-off)
- ๋ค์ค ๊ฐ์ฉ๊ตฌ์ญ(Multi-AZ) ๋ฌด์ค๋จ ์ด์คํ ๋ฐ ๋กค๋ง ๋ฐฐํฌ๋ฅผ ๋ฌ์ฑํ ์ ์์ด, ํธ์คํธ ๋ฌผ๋ฆฌ ์ฅ์ ์ ์๋น์ค ์ ์ฒด ์ ์ (SPOF) ๋ฆฌ์คํฌ์ ๋ ธ์ถ๋จ
- ์ํ ๋์ฑ
(Mitigation)
- AWS CloudWatch Status Check Metric Alarms๋ฅผ ๊ฒฐํฉํด ๋ฌผ๋ฆฌ ํ๋์จ์ด ๊ฒฐํจ ๋ฐ์ ์ 1๋ถ ์ด๋ด์ ์ธ์คํด์ค๋ฅผ ์ ์ ๋ฌผ๋ฆฌ ํธ์คํธ๋ก ์๋ ๋ณต์(Auto Recovery) ๋ฐ EIP ์ฌ๋ฐ์ธ๋ฉ ์ฒ๋ฆฌ
- ์ต์ ํ ์กฐ์น (Optimization)
- ์ $15~20 ์ด์์ RDS ์๋น์ค ๋น์ฉ ์ ๊ฐ์ ์ํด EC2 ๋ด๋ถ Docker Compose ํ๊ฒฝ์์ PostgreSQL ์ปจํ ์ด๋๋ฅผ ์ง์ ๊ฐ๋
- ๊ธฐ์ ์ ์ ์ฝ (Trade-off)
- AWS RDS์ ์์ ๊ด๋ฆฌํ ์ด์คํ ๋ณต๊ตฌ ๋ฐ ์์ ๋ณต๊ตฌ(PITR) ํธ์์ฑ์ ์์คํ์์ผ๋ฉฐ, ์ฌํด ๋ณต๊ตฌ ์ ๋ฐฑ์ ๋คํ ๊ธฐ๋ฐ ์๋ ๋ณต์ ์ฒ๋ฆฌ๊ฐ ์๊ตฌ๋จ์ ๋ฐ๋ผ ๋ณต๊ตฌ ๋ชฉํ ์๊ฐ(RTO) ์ฝ 20๋ถ ๋ฐ ์ต๋ ๋ฐ์ดํฐ ์์ค ํ๊ณ(RPO) 6์๊ฐ์ผ๋ก ์กฐ์ ๋จ
- ์ํ ๋์ฑ
(Mitigation)
- 6์๊ฐ ์ฃผ๊ธฐ DB dump ๋ฐ์ดํฐ๋ฅผ S3 ๋ ๋ฆฝ ๋ฐฑ์ ๋ฒํท์ผ๋ก ์ ์กํ๋ ์ ์คํฌ๋ฆฝํธ์ Cron ๋ฐฐํฌ ๋ฐ 30์ผ ๊ฒฝ๊ณผ ๋ฐฑ์ ์๋ ํ๊ธฐ ์ ์ฑ ์ฐ๋
- Terraform/Ansible ์ฝ๋ํ๋ฅผ ํตํด ์ ์ฒด ์ ์ค ๋ฐ์ ์์๋ 5๋ถ ์ด๋ด ์ธํ๋ผ ์ฌ์ค์น ๋ฐ ๋ฐ์ดํฐ ์๋ ๋ณต๊ตฌ ์ ์ฐจ ์๋ฆฝ (ROA)
- ์ต์ ํ ์กฐ์น (Optimization)
- ๊ฐ๋ฐ/๊ฒ์ฆ์ฉ Staging EC2 ์ธ์คํด์ค๋ ๋ถํ์ํ ์ปดํจํ ์์ ์๊ธ ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด ํ์์ ์ค์ง(Stopped) ์ํ ์ ์ง
- ์ํฌํ๋ก์ฐ ์ฐ๋ (Workflow / Mitigation)
- GitHub Actions
deploy-staging์คํ ์ AWS CLI๋ก ์ธ์คํด์ค๋ฅผ ์๋์ผ๋ก ๊ธฐ๋(Start)ํ์ฌ ๋ฐฐํฌ ๋ฐ Playwright ๋ธ๋ผ์ฐ์ E2E ํ ์คํธ ๊ฒ์ฆ ์งํ - ๊ฒ์ฆ ์๋ฃ ํ ์ผ๊ฐ(๋งค์ผ ์๋ฒฝ 2์ KST)์ ์ ์ง ์๋ํ ์ค์ผ์ค(staging-cleanup.yml)์ ๊ตฌ๋ํ์ฌ ๋น์ฉ ํจ์จ์ฑ ํ๋ณด
- GitHub Actions
๋ณธ ํ๋ก์ ํธ๋ ์๋น์ค ๋ฌด๊ฒฐ์ฑ๊ณผ ํธ์คํธ ์์คํ ๋ณดํธ๋ฅผ ์ํด AWS Well-Architected Framework์ ๋ณด์ ๊ธฐ๋ฅ(Security Pillar) ์ค๊ณ ๊ฐ์ด๋๋ผ์ธ์ ๋ถํฉํ๋ 3๋ ๋ณด์ ์ ์ด ์ ์ฑ ์ ๊ตฌํํ์ต๋๋ค.
- SSM Session Manager ๋์
- ๋ฌด์์ ๋์ ๊ณต๊ฒฉ๊ณผ SSH ํค ์ ์ถ ๋ฆฌ์คํฌ๊ฐ ๋์ ํธ์คํธ SSH(22) ํฌํธ๋ฅผ ์ธ๋ฐ์ด๋ ๋ณด์ ๊ทธ๋ฃน์์ ์์ ์ฐจ๋จ
- IAM ์๊ฒฉ ์ฆ๋ช ๊ธฐ๋ฐ์ AWS Systems Manager Session Manager๋ฅผ ๊ฒฝ์ ํ๋ ์ธ์ ํต์ ๋ง ํ์ฉ
- Ansible SSM ํฐ๋ ์บก์ํ
- ํธ์คํธ์ 22๋ฒ ํฌํธ๋ฅผ ์๊ฒฉ ๊ฐ๋ฐฉํ์ง ์๊ณ , ๋ก์ปฌ ๋ฐ ๋ฐฐํฌ ๋ฌ๋ ํ๊ฒฝ์์
aws ssm start-sessionํ๋ก์ ๋ช ๋ น(ProxyCommand)์ SSH ํฐ๋๋ก ์บก์ํ - ํด๋น ํฐ๋ ๋ด๋ถ์์ ๊ธฐ์กด SSH ์ธ์ฆ ํค(PEM)๋ฅผ ํ์ฉํ 2์ฐจ ์ธ์ฆ์ ํต๊ณผํด์ผ๋ง Ansible Playbook ๊ฐ๋์ด ๊ฐ๋ฅํ๋๋ก ์ด์ค ๋ฐฉ์ด์ ๊ตฌ์ถ (์์ธ
hosts.ini๊ตฌ์ฑ์ ๋ถ๋ก 5.1.3. AWS SSM Session Manager Setup ์ฐธ๊ณ )
- ํธ์คํธ์ 22๋ฒ ํฌํธ๋ฅผ ์๊ฒฉ ๊ฐ๋ฐฉํ์ง ์๊ณ , ๋ก์ปฌ ๋ฐ ๋ฐฐํฌ ๋ฌ๋ ํ๊ฒฝ์์
- OIDC Keyless Authentication
- GitHub Actions ๋ฌ๋ ๋ฐฐํฌ ์ ํ๋์ฝ๋ฉ๋ AWS API Access Key ์ฌ์ฉ์ ์ ๋ฉด ๋ฐฐ์
- GitHub OIDC(OpenID Connect) ์ฐ๋์ ์๋ฆฝํ์ฌ ๋ฐฐํฌ ์์ ์ AWS STS๋ก๋ถํฐ 1ํ์ฉ ๋จ๊ธฐ ์๊ฒฉ ์ฆ๋ช
(
AssumeRole)์ ํ๋ํจ์ผ๋ก์จ ์ ์ถ ๊ฒฝ๋ก ์์ฒ ์ ๊ฑฐ
- ์๋น์ค ์์ค ์ต์ ๊ถํ ์ ์ฑ
(Least Privilege)
- ํ ๋ผํผ ๋ฐ Ansible ๋ฐฐํฌ ๋ฒ์์ ์ ํํ ๋ถํฉํ๋ ์๋น์ค ์์ค ์ต์ ๊ถํ ์ ์ฑ (Staging/Production ๋ณ ์ปค์คํ IAM Policy) ๋ฐ์ธ๋ฉ
- ํ์ฉ ์์ ์ด์ธ์ ํ ์๋น์ค ์์(์: RDS, Lambda, KMS ๋ฑ) ๊ด๋ฆฌ๋ฅผ ์์ฒ ๋ฐฐ์ ํ์ฌ ์ํ ๋ฐ๊ฒฝ ์ฐจ๋จ
EC2 ํธ์คํธ ๋ฐ CI/CD ํ์ดํ๋ผ์ธ ๊ฐ๊ฐ์ ์คํ ์ฃผ์ฒด๋ณ๋ก ์ค์ ์ ์ฉ๋ IAM ๊ถํ๊ณผ OIDC ๋จ๊ธฐ ์๊ฒฉ ์ฆ๋ช ๊ธฐ๋ฐ์ ์์ ํต์ ์ํคํ ์ฒ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
C4Component
title Component Diagram for Identity & Access Management (Level 3: Security & IAM)
Container(runner, "GitHub Actions Runner", "GitHub Cloud", "Deploys infra/app using temporal credentials.")
Container(ec2, "EC2 App Server", "AWS EC2", "Runs application stack and background helpers.")
System_Boundary(iam, "AWS IAM (Identity & Access Management)") {
Component(oidc, "OIDC Provider", "token.actions.githubusercontent.com", "Verifies GitHub Actions runner token.")
Component(run_role, "CI/CD Runner IAM Role", "IAM Role", "Assumed via OIDC federation.")
Component(host_role, "EC2 Host IAM Role", "IAM Role (Instance Profile)", "Attached to EC2 hosting profile.")
Component(tf_policy, "Terraform & Deploy Policy", "Customer Managed Policy", "Allows EC2, VPC, S3, DynamoDB, Route 53, CloudFront management.")
Component(ssm_policy, "SSM Managed Policy", "AWS Managed Policy", "Allows SSM Systems Manager connectivity.")
Component(cw_policy, "CloudWatch Log Policy", "Customer Managed Policy", "Allows log groups/streams push operations.")
Component(s3_back_policy, "S3 Backup Write Policy", "Customer Managed Policy", "Allows database dump upload.")
}
System_Boundary(aws_resources, "AWS Resources Boundary") {
System(s3_tf, "S3 tfstate & deploy Bucket", "Object Storage")
System(ddb_lock, "DynamoDB tfstate lock Table", "NoSQL Database")
System(cf_cdn, "CloudFront CDN / Route 53", "Edge Routing")
System(cw_logs, "CloudWatch Logs", "Telemetry Store")
System(s3_back, "S3 Backup Bucket", "Object Storage")
}
Rel(runner, oidc, "1. Authenticates", "OIDC Web Identity Token")
Rel(oidc, run_role, "2. Issues short-term session", "AssumeRoleWithWebIdentity")
Rel(run_role, tf_policy, "3. Binds permissions")
Rel_D(tf_policy, ec2, "Manage VPC & Host", "AWS API")
Rel_D(tf_policy, s3_tf, "Read/Write tfstate & deploy site", "AWS API")
Rel_D(tf_policy, ddb_lock, "Acquire/Release Lock", "AWS API")
Rel_D(tf_policy, cf_cdn, "Invalidate cache / Update DNS", "AWS API")
Rel(ec2, host_role, "4. Obtains profile context", "Instance Metadata Service (IMDS)")
Rel(host_role, ssm_policy, "5. Binds permissions")
Rel(host_role, cw_policy, "5. Binds permissions")
Rel(host_role, s3_back_policy, "5. Binds permissions")
Rel_D(ssm_policy, ec2, "Establish secure tunnel", "SSM Tunnel")
Rel_D(cw_policy, cw_logs, "Push application stdout", "CloudWatch API")
Rel_D(s3_back_policy, s3_back, "Upload daily DB dump", "S3 API")
| ์ฃผ์ฒด (Principal) | ์ธ์ฆ ๋ฐฉ์ (Auth Type) | ์ฐ๊ฒฐ๋ IAM ์ ์ฑ ๋ฐ ๊ถํ (IAM Policies) | ์ฃผ์ ์ญํ ๋ฐ ๋น๊ณ (Key Role) |
|---|---|---|---|
| EC2 Host Role | Instance Profile | AmazonSSMManagedInstanceCoreStaging: CloudWatchAgentServerPolicy (๊ด๋ฆฌํ)Production: nemologic-cloudwatch-log-policy (์ปค์คํ
)s3_backup_policy (์ปค์คํ
) |
SSM ํฐ๋๋ง ํ์ฑํ, CloudWatch ๋ก๊ทธ ์ค์๊ฐ ํฌ์๋ฉ(Staging/Production ๋ณ ์ ์ฑ ์ฐจ๋ฑ ์ ์ฉ), DB ๋ฐฑ์ S3 ์ ๋ก๋ ๊ถํ ์ ์ด |
| CI/CD Runner (GitHub) | AWS OIDC (Keyless) | nemologic-staging-github-policynemologic-production-github-policy (์ปค์คํ
) |
sts:AssumeRoleWithWebIdentity๋ฅผ ํตํด GitHub Actions OIDC ํ ํฐ์ผ๋ก 1ํ์ฉ ๋จ๊ธฐ ์๊ฒฉ ์ฆ๋ช
์ ํ๋ํ์ฌ Terraform ๋ฐ ๋ฐฐํฌ ์ํ (Secret Key ํ๋์ฝ๋ฉ ๋ฐฐ์ ๋ฐ ์ต์ ๊ถํ ์๋ฆฝ) |
C4Container
title Container Diagram for rogic.io (Level 2: Network & Containers)
Person(player, "Player / User", "Accesses the puzzle game through a web browser.")
Person(sre, "SRE / QA (CI/CD)", "Deploys and tests the staging application.")
System_Boundary(aws, "AWS Cloud (ap-northeast-2)") {
System_Boundary(vpc_prod, "Production VPC (10.0.0.0/16)") {
System_Boundary(fnet_prod, "frontend-net (Docker Bridge)") {
Container(nginx, "Nginx Reverse Proxy", "Docker Container", "SSL/TLS termination, API routing, and Bearer token auth validation.")
}
System_Boundary(bnet_prod, "backend-net (Docker Bridge)") {
ContainerDb(postgres, "PostgreSQL Database", "Docker Container", "Persists puzzle templates, user logs, clear history, and user stats.")
}
Container(spring, "Spring Boot App", "Docker Container (GraalVM) [frontend-net & backend-net]", "Handles business logic, daily puzzle scheduling, rating, and XP leaderboard.")
Rel(nginx, spring, "Proxy API requests", "HTTP / Port 8080 [frontend-net]")
Rel(spring, postgres, "Reads/Writes state", "JPA & JDBC / Port 5432 [backend-net]")
}
System_Boundary(vpc_stage, "Staging VPC (10.1.0.0/16)") {
System_Boundary(fnet_stg, "frontend-net (Stage Bridge)") {
Container(nginx_stg, "Nginx Reverse Proxy (Stage)", "Docker Container", "Staging SSL/TLS termination and API routing.")
}
System_Boundary(bnet_stg, "backend-net (Stage Bridge)") {
ContainerDb(postgres_stg, "PostgreSQL Database (Stage)", "Docker Container", "Persists isolated staging state.")
}
Container(spring_stg, "Spring Boot App (Stage)", "Docker Container (JVM) [frontend-net & backend-net]", "Staging application runtime environment.")
Rel(nginx_stg, spring_stg, "Proxy API requests", "HTTP / Port 8080 [frontend-net]")
Rel(spring_stg, postgres_stg, "Reads/Writes state", "JPA & JDBC / Port 5432 [backend-net]")
}
Container(cloudfront, "Amazon CloudFront", "AWS CDN", "Distributes static web assets with low latency.")
Container(s3, "Amazon S3", "AWS Bucket Storage", "Hosts Vite/Vue built static files (HTML, JS, CSS).")
}
Rel(player, cloudfront, "Fetches static web pages", "HTTPS / Port 443")
Rel(cloudfront, s3, "Refreshes cache from origin", "S3 Protocol")
Rel(player, nginx, "Calls API endpoints", "HTTPS / Port 443")
Rel(sre, nginx_stg, "Calls API endpoints (Stage) during tests", "HTTPS / Port 443")
- ๋ฌผ๋ฆฌ ๊ฒฉ๋ฆฌํ VPC ๊ตฌ์ฑ
- Staging VPC(
10.1.0.0/16)์ Production VPC(10.0.0.0/16)๋ฅผ ๊ฐ๋ณ ์๋ธ๋ท ๋์ญ๊ณผ ๋ ๋ฆฝ ์ธํ๋ผ๋ง์ผ๋ก ๋ถ๋ฆฌ ํ๋ก๋น์ ๋ - ๋ง๊ฐ ๊ต์ฐจ ์ ๊ทผ์ ์์ฒ ์ฐจ๋จํ์ฌ ํ ์คํธ ํ๊ฒฝ์ ๋ถ์์ ์ฑ์ด ์ด์๊ณ์ ์ ์ด๋์ง ์๋๋ก ๊ฒฉ๋ฆฌ ์์ ์ฑ ํ๋ณด
- Staging VPC(
- ๋ค๊ณ์ธต ๋์ปค ๋ธ๋ฆฌ์ง ๋คํธ์ํฌ ๊ฒฉ๋ฆฌ
- ๋จ์ผ EC2 ๋ด๋ถ ์ปจํ
์ด๋ ํต์ ์ ์ธํฐ๋ท ๊ฐ๋ฐฉ์ ์ธ Nginx ํ๋ก์(
frontend-net)๊ฐ DB(backend-net)์ ์ง์ ์ ๊ทผํ ์ ์๋๋ก ๊ฐ์ ๋คํธ์ํฌ ๋ถ๋ฆฌ - ๋ฐฑ์๋ API ์ปจํ ์ด๋๋ง ์์ชฝ ๋ธ๋ฆฌ์ง ๋คํธ์ํฌ์ ๋์ ์์๋์ด ๊ฐ๊ต ์ญํ ์ ์ ๋ดํ๊ฒ ํจ์ผ๋ก์จ ํก์ ์ด๋(Lateral Movement) ์ํ ์ ํ
- ๋จ์ผ EC2 ๋ด๋ถ ์ปจํ
์ด๋ ํต์ ์ ์ธํฐ๋ท ๊ฐ๋ฐฉ์ ์ธ Nginx ํ๋ก์(
- Database ์์๋ฐ์ด๋ ์์ ์ฐจ๋จ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ
์ด๋๊ฐ ์์ฃผํ๋
backend-net๋ธ๋ฆฌ์ง๋ง์internal: true์ต์ ์ธ๋ผ์ธ ์ง์ - ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ธ๋ถ ์ธํฐ๋ท ์์๋ฐ์ด๋ ์๋๋ฅผ ๋ด์ํ์ฌ RCE(์๊ฒฉ ์ฝ๋ ์คํ) ์นจํฌ ์ ๋ฆฌ๋ฒ์ค ์ปค๋ฅ์ ๋ฐ ๋ฐ์ดํฐ ๋ฌด๋จ ์ ์ถ(Exfiltration) ์๋ ์์ฒ ์ฐจ๋จ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ
์ด๋๊ฐ ์์ฃผํ๋
- ์ต์ ์ธ๋ฐ์ด๋ ํฌํธ ์ ํ
- Staging ๋ฐ Production ํ๊ฒฝ ๋ชจ๋ ์ธ๋ถ ์๋น์ค ๋ฐ ๋ชจ๋ํฐ๋ง ์ฐ๋์ ์ํ Nginx ํฌํธ(80, 443)๋ง ์ธ๋ถ ์ธ๋ฐ์ด๋ ๊ฐ๋ฐฉ
- SSH(22), Spring API(8080), Vite Frontend ๊ฐ๋ฐ(5173) ํฌํธ๋ ๋ณด์ ๊ทธ๋ฃน ๊ท์น์์ ์์ ํ ์ ์ธํ์ฌ ์ธ๋ถ ์ ๊ทผ ์ฐจ๋จ
- ์๊ฒฉ ๋ฉํธ๋ฆญ ์์ง ํ๋ก์ ์ค์ฌ
- Grafana Cloud Mimir์ ์๊ฒฉ ํ๋ก๋ฉํ ์ฐ์ค ์์ง๊ธฐ๊ฐ ๋ฉํธ๋ฆญ์ ์์ง(Pull)ํ ๋ ์ธ๋ถ Actuator ํฌํธ(8080) ์ง์ ํธ์ถ ์ฐจ๋จ
- Nginx HTTPS(443) ์ธํฐํ์ด์ค๋ก ์คํฌ๋ํ์ ์์ฒญํ๋ฉด, Nginx๊ฐ Bearer ํ ํฐ ๋ณด์ ๊ฒ์ฆ์ ์๋ฃํ ํต์ ์ ํํด ๋ก์ปฌ ๋ฃจํ๋ฐฑ๋ง์
/actuator/prometheus๋ก ํฌ์๋ฉ ์ค์ฌ
- ์ปจํ
์ด๋ ๋ณด์ ๋ก๋๋งต
- ํฅํ ์ปจํ ์ด๋ ๋ด๋ถ ์ ํ๋ฆฌ์ผ์ด์ ์ Non-root User ์คํ ๊ถํ ์ ํ ๋ฐ Read-Only root ํ์ผ์์คํ ์ ํ ์ ์ฉ ์์
- DB ๋ฐฑ์
์ ํธ์คํธ ๋จ์์ Docker API ํ์ค ์ถ๋ ฅ ํ์ดํ๋ผ์ธ(
docker exec pg_dump)์ผ๋ก ์์ ํ๊ฒ ์ค์ฌ ์ฒ๋ฆฌํ์ฌ ๋ฐฑ์ ๋ฌด๊ฒฐ์ฑ ์ ์ง
-
๋ณด์ ๊ทธ๋ฃน ์ธ๋ฐ์ด๋ ์ ์ด (Security Group Ingress/Egress Rule)
์ธ๋ถ ์ธํฐ๋ท๊ณผ์ ๊ฒฝ๊ณ์ ํฌํธ๋ฅผ ์ ์ดํ๊ณ , ์์๋ฐ์ด๋ ์ ์ก ํธ๋ํฝ ๊ท๊ฒฉ์ ๋ช ํํ ๊ณ ์ ํฉ๋๋ค.ํ์ฉ ํฌํธ (Port) ํ๋กํ ์ฝ (Protocol) ์์ค (Source) ๋ชฉ์ ๋ฐ ๋์ ์๋น์ค 80 TCP 0.0.0.0/0Nginx HTTP ์น ์๋ฒ (HTTPS 301 ๋ฆฌ๋ค์ด๋ ํธ์ฉ) 443 TCP 0.0.0.0/0Nginx HTTPS ๋ณด์ ์น ์๋น์ค ๋ฐ API ํต์ (๋ชจ๋ํฐ๋ง ์คํฌ๋ํ ํฌํจ) ํ์ฉ ํฌํธ (Port) ํ๋กํ ์ฝ (Protocol) ๋์ (Destination) ๋น๊ณ All All 0.0.0.0/0ํจํค์ง ์ ๋ฐ์ดํธ, ์ธ๋ถ API ํธ์ถ ๋ฐ DB ๋ฐฑ์ S3 ์ ๋ก๋์ฉ
- ์ธ์ฆ์ ์๋ ๊ฐฑ์ ์๋ํ
- Let's Encrypt ๋ฌด๋ฃ SSL ์ธ์ฆ์๋ฅผ ๋ฐ๊ธ๋ฐ์ HTTPS(443) ํต์ ๋ฐ HTTP(80) 301 ๋ฆฌ๋ค์ด๋ ํธ ์ ์ฑ ๊ตฌํ
- 3๊ฐ์ ์ฃผ๊ธฐ ๋ง๋ฃ ์ ์ ์ธ์ฆ์๋ฅผ ์๋ ๊ฐฑ์ ํ ์ ์๋๋ก pre/post ์ ์คํฌ๋ฆฝํธ ํ ์ Certbot ๋ฐ๋ชฌ์ ์ฐ๋ํ์ฌ ๋ง๋ฃ ๋ค์ดํ์ ์๋ฐฉ
- ์๊ฒฉ ์ํ ํ์ ๋ณด์
- AWS S3 ๋ฒํท๊ณผ DynamoDB ํ
์ด๋ธ(
LockID)์ ํ ๋ผํผ Backend๋ก ์ง์ ํ์ฌ ํ์ ๋ฐ ๋ฐฐํฌ ์ ์ํ ํ์ผ(State)์ ๋์ ์์ ์ถฉ๋ ๋ฐ ์์ ์์ฒ ๋ฐฉ์ง - ์ํ ํ์ผ ์ํธํ ์ ์ฑ ์ ์ฐ๋ํ์ฌ ์ธํ๋ผ ํ์ ์์ฐ ์ ๋ณด ๋ณดํธ
- AWS S3 ๋ฒํท๊ณผ DynamoDB ํ
์ด๋ธ(
๋ณธ ํ๋ก์ ํธ๋ ์์คํ ๊ฐ์ฉ์ฑ๊ณผ ์งํ ์์ง ๋ถํ ์ต์ํ ํต์ ๋ฅผ ์ํด ์ ๊ณ ํ์ค ๋ชจ๋ํฐ๋ง ํต์ฌ ์์ญ(Metrics, Logs, Alerting & SLO)์ ๊ด์ ์ํคํ ์ฒ๋ก ๊ตฌ์ถํ์ต๋๋ค.
C4Container
title Telemetry Diagram for rogic.io (Level 3: Observability & Alerting)
System_Boundary(host, "AWS EC2 Instance (Target Host)") {
Container(nginx, "Nginx Reverse Proxy", "Docker", "Bearer Token Authentication Endpoint.")
Container(spring, "Spring Boot Backend", "Docker (GraalVM)", "Exposes Prometheus Actuator Metrics.")
Rel(nginx, spring, "Forwards prometheus scraping requests", "Port 8080")
}
System_Boundary(grafana_cloud, "Grafana Cloud Platform") {
Container(grafana, "Grafana Dashboards", "SaaS Dashboard", "Visualizes SLA metrics, CPU, Memory, and log groups.")
Container(prometheus, "Prometheus / Mimir", "SaaS TSDB", "Scrapes metrics via Agentless Pull architecture.")
Rel(grafana, prometheus, "Queries metrics data")
}
System_Boundary(observability, "AWS Management & Alerting") {
Container(cw, "Amazon CloudWatch", "AWS Logging", "Collects application stdout log streams via awslogs driver.")
Container(sns, "AWS SNS Topic", "AWS Alerting", "Triggers notifications based on metric filter threshold alarms.")
Person(sre, "SRE Developer", "Receives real-time incident warning emails.")
Rel(cw, sns, "Metric Filter Threshold Alarmed")
Rel(sns, sre, "Sends warning email notification")
}
Rel(prometheus, nginx, "Scrapes metrics (Agentless Pull)", "HTTPS Bearer Auth / Port 443")
Rel(spring, cw, "Streams application logs", "awslogs driver")
- Agentless Pull ์ํคํ
์ฒ ์๋ฆฝ
- ํธ์คํธ ๋ด๋ถ CPU/๋ฉ๋ชจ๋ฆฌ ์์์ ์๋ชจํ๋ ๋ณ๋ ์์ง ์์ด์ ํธ(Grafana Alloy ๋ฑ)๋ฅผ ์์ ํ ๋ฐฐ์
- Nginx ๋ฆฌ๋ฒ์ค ํ๋ก์ ๋จ์์
Authorization: Bearerํค๋ ํ ํฐ์ ์์ ๋์กฐ ๊ฒ์ฆํ๋ ๊ฐ์ ๋ผ์ฐํ ๊ฒฝ๋ก๋ฅผ ๊ฐ๋ฐฉ - ์ธ๋ถ Grafana Cloud์ Prometheus/Mimir ์๋ฒ๊ฐ ์ ๊ธฐ์ ์ผ๋ก ์งํ๋ฅผ ์ง์ Scrape(Scraping)ํ๋๋ก ์ค๊ณํ์ฌ ์์ด์ ํธ ๊ตฌ๋ ๋ถํ๋ฅผ 0์ผ๋ก ํต์
- awslogs Docker ๋๋ผ์ด๋ฒ ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ
- ๊ฐ๋ณ ์ปจํ
์ด๋ ๋ด๋ถ ์ฝ์ ์ถ๋ ฅ์ ๋์คํฌ ํ์ผ ๋์ AWS CloudWatch Logs(
/aws/ec2/nemologic)๋ก ์ฆ์ ๋ฆฌ๋ค์ด๋ ํธ ํฌ์๋ฉ - ํธ์คํธ ๋ก์ปฌ ๋ด์ ์์ ๋ก๊ทธ๋ฅผ ์ถ์ ํ์ง ์์ ๋์คํฌ ๊ณต๊ฐ ๊ณ ๊ฐ ๋ฐ I/O ๋ณ๋ชฉ ๋ฆฌ์คํฌ ์ฌ์ ๊ฒฉ๋ฆฌ
- ๊ฐ๋ณ ์ปจํ
์ด๋ ๋ด๋ถ ์ฝ์ ์ถ๋ ฅ์ ๋์คํฌ ํ์ผ ๋์ AWS CloudWatch Logs(
- ์ก์ธ์ค ์งํ ๋ก๊ทธ ํํฐ๋ง
- ํฌ์ค์ฒดํฌ ๋ฐ ์ฃผ๊ธฐ์ ์ธ ํ๋ก๋ฉํ
์ฐ์ค ๋ฉํธ๋ฆญ ์์ง API ํธ์ถ ๊ฒฝ๋ก์ Nginx Access Log ๋ก๊น
์ ๊ฐ์ ์ค์ง(
access_log off;) ์ฒ๋ฆฌ - ๋ถํ์ํ ๊ด์ ํธ๋ํฝ์ ์ํ ์คํ ๋ฆฌ์ง ๋ญ๋น ๋ฐ CPU ์๋ชจ ํต์
- ํฌ์ค์ฒดํฌ ๋ฐ ์ฃผ๊ธฐ์ ์ธ ํ๋ก๋ฉํ
์ฐ์ค ๋ฉํธ๋ฆญ ์์ง API ํธ์ถ ๊ฒฝ๋ก์ Nginx Access Log ๋ก๊น
์ ๊ฐ์ ์ค์ง(
- ์ฑ๊ฐํฌ๋ฅด/์๋๋/๋์ฟ 3์ค ๊ฐ์ฉ์ฑ ๊ด์
- Grafana Cloud Synthetic Monitoring ํ๋ก๋ธ๋ฅผ ํตํด ๋ค์ค ๊ธ๋ก๋ฒ ๋ฆฌ์ ์ฃ์ง(์ฑ๊ฐํฌ๋ฅด, ์๋๋, ๋์ฟ)์์ 1๋ถ ๊ฐ๊ฒฉ์ผ๋ก
/actuator/healthํฌ์ค์ฒดํฌ ๋ค์ค ๋ชจ๋ํฐ๋ง ์ํ - ๋จ์ผ ์ง์ ํ๋ก๋ธ ์ค๋ฅ์ ๋ฐ๋ฅธ ์คํ์ ๋ฐฉ์งํ๊ณ ๋ค์ค ๊ฐ์ ๊ฐ์ฉ์ฑ ๊ฒ์ฆ ์ฒด๊ณ ๊ตฌํ
- Grafana Cloud Synthetic Monitoring ํ๋ก๋ธ๋ฅผ ํตํด ๋ค์ค ๊ธ๋ก๋ฒ ๋ฆฌ์ ์ฃ์ง(์ฑ๊ฐํฌ๋ฅด, ์๋๋, ๋์ฟ)์์ 1๋ถ ๊ฐ๊ฒฉ์ผ๋ก
- AWS SNS ๊ฒฝ๋ณด ๋ฉ์ผ ์ ์ก
- CloudWatch Logs Metric Filter ์๊ณ์น ์ด๊ณผ ์ฅ์ ๊ฐ์ง ์ AWS SNS ํ ํฝ์ ํธ๋ฆฌ๊ฑฐํ์ฌ SRE ๋ฉ์ผ๋ก ์ฅ์ ์ธ์๋ํธ ์ฆ์ ์ ํ
- ํตํฉ SLA ๋์๋ณด๋ ์๊ฐํ (current_dashboard.json)
- ํต์ฌ ๊ฐ์ฉ์ฑ ์งํ(Uptime SLA, Incident Count, MTTR, MTBF)๋ฅผ Grafana ๋์๋ณด๋ ์๋จ ๋จ์ผ ํ 4์ด KPI ์นด๋๋ก ์ผ๊ด ๊ด์ ๊ฐ๋ฅํ๋๋ก ๋์ ์ฐ๋ ๊ตฌ์ฑ
- ๋ ์ด์์ ๊ตฌ์ฑ์ฉ ์์ ๋งํฌ: Grafana Live Public Dashboard (๋ฏผ๊ฐ ๋ฉํธ๋ฆญ ๋ฐฐ์ ๋ฐ๋ชจ์ฉ ๊ตฌ์ฑ)
- ์์ธ ๊ด์ PromQL ์์ ๋ฐ ์ฟผ๋ฆฌ ๊ตฌํ์ ๋ถ๋ก 5.2. PromQL Query Formulations (SLO Metrics) ์ฐธ๊ณ
- ๋ฐฐ๊ฒฝ
- ์ธํ๋ผ ๋น์ฉ ๊ทน ์ต์ํ(์ $11.45 ๊ตฌ์ฑ)๋ฅผ ์ํด t3a.nano ์ธ์คํด์ค(512MB RAM) ํ๊ฒฝ์ ์ ํํ์์ผ๋, ๋ชจ๋ํฐ๋ง ์์ง ์์ด์ ํธ(Grafana Alloy)์ ๋ฉ๋ชจ๋ฆฌ ์ ์ (100MB+)์ ๋ธ๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌ ์์ ์ Spring Boot ์ปจํ ์ด๋ 2๊ฐ๊ฐ ์ผ์์ ์ผ๋ก ๋์์ ๊ธฐ๋ํ๋ฉด์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ํ๊ณ๋ฅผ ์ด๊ณผํ์ฌ OOM ๋ฐ CPU ์ค๋ ์ฑ ์ฅ์ ๊ฐ ๋น๋ฒํ ๋ฐ์ํจ.
- ํนํ ์ต์ด ๋ฐฐํฌ ์(์ฝ๋ ์คํํธ) ๋ก์ปฌ ์บ์ ์ด๋ฏธ์ง๊ฐ ์๋ ์ํ์์ ์๋ฐฑ MB ์๋น์ Base Image ๋ค์ด๋ก๋ ๋ฐ ์์ถ ํด์ ๊ฐ ๊ฒน์ณ ๋์คํฌ I/O ๋ณ๋ชฉ์ด ๋ฐ์, ๋ฐฐํฌ ํ์ดํ๋ผ์ธ์ด 1์๊ฐ ์ด์ ๋ฉ์ถฐ์๋ค๊ฐ ์ค๋จ๋๋ ํ์์ด ์ผ์ด๋จ.
- ํด๊ฒฐ ๋ฐฉ์
- ์์ ์ง๋จ ๋ฐ ์๊ณ ์งํ ์ด์: SSH ์ง์ฐ ์ํฉ์์ ๋ฆฌ๋
์ค
top๋ฐvmstat๋ช ๋ น์ด๋ฅผ ํ์ฉํด CPUidle0% ์๋ ด ๋ฐ I/O Wait(wa)์ ๊ธ๊ฒฉํ ์์น์ ๋ฐ๋ฅธ ๋์คํฌ/CPU ์ค๋์ฑ ์ํ๋ฅผ ์ ํํ ๊ท๋ช ํ์ต๋๋ค. ์ง๋จ ๊ฒฐ๊ณผ๋ฅผ ํ ๋๋ก Grafana Cloud ๋ชจ๋ํฐ๋ง ๋์๋ณด๋์wa๋ฐidle์งํ๋ฅผ ๊ด์ธก ๊ฐ๋ฅํ๋๋ก ์ถ๊ฐ ์ด์ํ์ต๋๋ค. - ์์ง ์์ด์ ํธ ๊ฑท์ด๋ด๊ธฐ: ์์ ์ ์ ๊ฐ ํฐ Alloy ๋ฐ๋ชฌ์ ์ ๊ฑฐํ๊ณ Grafana Mimir๊ฐ Nginx ํ๋ก์๋ฅผ ํตํด ์งํ๋ฅผ ์ง์ Scrapeํ๋ Agentless Pull ๊ตฌ์กฐ๋ก ์ ๋ฉด ์ ํํ์ต๋๋ค.
- ๋ฐํ์ ์ด๊ฒฝ๋ํ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์ค์: Spring Boot ๊ตฌ๋ ํํ๋ฆฐํธ๋ฅผ 30MB ์ดํ๋ก ์์ถํ๊ธฐ ์ํด GraalVM Native Image ์ปดํ์ผ ์ต์ ์ ๋์ ํ๊ณ , 2GB ํฌ๊ธฐ์ SWAP ํํฐ์ ์ ํ์ฑํํ์ฌ ์ปจํ ์ด๋ ๊ต์ฒด ์๊ฐ์ ์ผ์์ ๋ฉ๋ชจ๋ฆฌ ํผํฌ๋ฅผ ์์ถฉํ์ต๋๋ค.
- ๋์ปค ์ด๋ฏธ์ง ์บ์ ํ์ฉ: ์ฒซ ์ฝ๋ ๋ฐฐํฌ ์ดํ์๋ ๋์ปค ๋ ์ง์คํธ๋ฆฌ ๋ก์ปฌ ์ด๋ฏธ์ง ์บ์ฑ ๋ฐ ๋ ์ด์ด ์ฌ์ฌ์ฉ ๋ฉ์ปค๋์ฆ์ ํตํด ์ด๋ฏธ์ง Pull/Extract ๋ถํ๊ฐ ๋ํญ ๋ฎ์์ ธ I/O ์ค๋์ฑ ๋ณ๋ชฉ์ด ์๋ ํด์๋๋๋ก ์ ๋ํ์ต๋๋ค. ์ถ๊ฐ์ ์ผ๋ก Docker ๋ฏธ์ฌ์ฉ ์ด๋ฏธ์ง/๋ณผ๋ฅจ ์ ๋ฆฌ๋ฅผ ์ํด ์ฃผ๊ธฐ์ GC ํฌ๋ก ํญ์ ๋ฐ์ธ๋ฉํ์ต๋๋ค.
- ์์ ์ง๋จ ๋ฐ ์๊ณ ์งํ ์ด์: SSH ์ง์ฐ ์ํฉ์์ ๋ฆฌ๋
์ค
- ๊ธฐ์ ์ ๊ตํ ๋ฐ ์์ฌ๊ฒฐ์ (Retrospective)
- ๊ทน๋จ์ ์ธ 512MB RAM ํ๊ฒฝ์์๋ ์์ง๋์ด๊ฐ ๋ฆฌ๋
์ค ์ ์์ค ๋๊ตฌ(
top,vmstat)๋ฅผ ํ์ฉํ ์ค์๊ฐ ๋ฆฌ์์ค ๊ฐ์ ๋ฐ ๋ฉํธ๋ฆญ ์ด์์ ๊ฑฐ์ณ ๋ณ๋ชฉ ์ง์ ์ ๊ณผํ์ ์ผ๋ก ๋ฐํ๊ณ ๊ทน๋ณตํ ์ฌ๋ก์ ๋๋ค. - ์์ ์ค์ผ์ผ์ ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐํ์ ์์ฒด๋ฅผ Nativeํํ๊ณ ๋์ปค ๋ก์ปฌ ์บ์ ๋ฉ์ปค๋์ฆ๊ณผ SWAP ์ค์ ์ ๊ฒฐํฉํ์ฌ, ์ต์ ๋น์ฉ ์๋ฒ์์๋ ๊ณ ๊ฐ์ฉ์ฑ ๋ฐ ๋ณต๊ตฌ ์งํฅ์ ์ด์(ROA)์ด ๊ฐ๋ฅํจ์ ์ค์ฆํ์ต๋๋ค.
- ๊ทน๋จ์ ์ธ 512MB RAM ํ๊ฒฝ์์๋ ์์ง๋์ด๊ฐ ๋ฆฌ๋
์ค ์ ์์ค ๋๊ตฌ(
๋ณธ ํ๋ก์ ํธ๋ ์ฝ๋ ํ์ ํตํฉ๋ถํฐ ์ด์๊ณ ์ค๋ฐฐํฌ๊น์ง์ ์์ ์ฃผ๊ธฐ๋ฅผ ์ ์ดํ๊ธฐ ์ํด 4๋จ๊ณ GitOps ๋ฐฐํฌ ์ํฌํ๋ก์ฐ๋ฅผ ์ ์ฉํ์ต๋๋ค.
stateDiagram-v2
direction LR
[*] --> CI : Git Push to main
state "1. Continuous Integration (CI)" as CI {
direction TB
state "Backend: Gradle Tests" as UnitB
state "Frontend: Vitest Tests" as UnitF
state "Infra: Ansible Lint" as Lint
[*] --> UnitB
[*] --> UnitF
[*] --> Lint
}
state "2. Continuous Delivery: Staging" as Staging {
direction TB
state "Build Backend (GHCR)" as BuildB
state "Build & S3 Sync Frontend" as BuildF
state "Terraform Apply Staging" as TFA_S
state "Deploy Backend via Ansible" as Deploy_S
state "Run Playwright E2E Tests" as E2E
[*] --> BuildB
[*] --> BuildF
BuildB --> TFA_S
BuildF --> TFA_S
TFA_S --> Deploy_S
Deploy_S --> E2E
}
state "3. Approval Gate" as Gate {
state "Pause for Admin Manual Approval" as Approve
[*] --> Approve
}
state "4. Continuous Deployment: Production" as Production {
direction TB
state "Terraform Apply Production" as TFA_P
state "Deploy Production via Ansible" as Deploy_P
state "Auto-SemVer Tag & Release" as Release
[*] --> TFA_P
TFA_P --> Deploy_P
Deploy_P --> Release
}
CI --> Staging : Validations Pass
Staging --> Gate : Playwright E2E Pass
Gate --> Production : Approved
Production --> [*] : Production Release Complete
- ๊ฒฝ๋ก ๊ธฐ๋ฐ ๋น๋ ์คํต (Path Filtering)
- ๋จ์ ๋งํฌ๋ค์ด ๋ฌธ์ ์์ (
*.md) ์ด๋ ๋ก์ปฌ ์ค์ ์ปค๋ฐ ์ ์ ์์๋ ๋น๋/์ปดํ์ผ ๋จ๊ณ๋ฅผ ์คํตํ์ฌ Actions ์ปดํจํ ์์ ๋ฐ ๋ฐฐํฌ ์๋ ์ต์ ํ
- ๋จ์ ๋งํฌ๋ค์ด ๋ฌธ์ ์์ (
- ๋ฐฐํฌ ๊ฒฝํฉ ๋ฐ ๋๊ธฐ ์๋ ์ทจ์ (Concurrency)
- Staging ํ๊ฒฝ ๋ฐฐํฌ๊ฐ ๊ธฐ๋ ์ค์ธ ์ํ์์ ์ ๊ท ๋ณ๊ฒฝ ์ปค๋ฐ์ด ์ถ๊ฐ ์ ์
๋๋ ์ฆ์ ์ด์ ๋ฐฐํฌ ๋จ๊ณ๋ฅผ ์๋ ๊ฐ์ ์ทจ์(
cancel-in-progress: true)ํ์ฌ ๋ฐฐํฌ ๊ผฌ์ ํ์ ์์ฒ ๋ฐฐ์
- Staging ํ๊ฒฝ ๋ฐฐํฌ๊ฐ ๊ธฐ๋ ์ค์ธ ์ํ์์ ์ ๊ท ๋ณ๊ฒฝ ์ปค๋ฐ์ด ์ถ๊ฐ ์ ์
๋๋ ์ฆ์ ์ด์ ๋ฐฐํฌ ๋จ๊ณ๋ฅผ ์๋ ๊ฐ์ ์ทจ์(
์์ ์ ์ธ ๋น๋ ํ์ผ ์์ฑ, ๋ฐฐํฌ ๊ฐ์ฉ์ฑ ํ๋ณด ๋ฐ ์ ๊ธฐ ๋ฆด๋ฆฌ์ฆ ์ฃผ๊ธฐ๋ฅผ ์ ์ดํ๊ธฐ ์ํ ์ฐ์ถ๋ฌผ๊ณผ ์์ฐ ๋ฐฐํฌ ๊ด๋ฆฌ ์ฒด๊ณ์ ๋๋ค.
- Actions Runner ์ปดํ์ผ ์คํ๋ก๋ฉ
- 512MB RAM ๊ทน๋จ ์ฌ์์ ์ง๋ ์ด์ ์๋ฒ์ ์ปดํ์ผ ๋ถํ ๊ณ ๊ฐ์ ์๋ฐฉํ๊ธฐ ์ํด ๋น๋ ๋ฐ ํจํค์ง ์ฐ์ฐ์ GitHub Actions ํด๋ผ์ฐ๋ ํ๊ฒฝ์ผ๋ก ์์ ์คํ๋ก๋ฉ (์์ธ ์ํ ๊ตฌ์กฐ๋ 1.2. Cost Optimization ๋ด Compute ์ต์ ํ ๋จ๋ฝ ์ฐธ๊ณ )
- Vite ์ ์ ์์ฐ ๋ค์ด๋ ํธ ๋๊ธฐํ
- ํ๋ก ํธ์๋ ๋น๋ ์ ๋ฌด๊ฑฐ์ด ๋์ปค ์ด๋ฏธ์ง ์บก์ํ ๋ฐฐํฌ ๋์ , ์ปดํ์ผ ์๋ฃ๋ ์ ์ ํ์ผ ๋ฒ๋ค(index.html, JS/CSS)์ AWS S3 ๋ฒํท์ผ๋ก ๋ค์ด๋ ํธ ๋๊ธฐํ(
aws s3 sync)ํ๊ณ CloudFront Edge Invalidation์ ํธ๋ฆฌ๊ฑฐํ์ฌ ์ด๊ฒฝ๋ CDN ์์ง ๋๋ฆฌ๋ฒ๋ฆฌ ์๋ฆฝ
- ํ๋ก ํธ์๋ ๋น๋ ์ ๋ฌด๊ฑฐ์ด ๋์ปค ์ด๋ฏธ์ง ์บก์ํ ๋ฐฐํฌ ๋์ , ์ปดํ์ผ ์๋ฃ๋ ์ ์ ํ์ผ ๋ฒ๋ค(index.html, JS/CSS)์ AWS S3 ๋ฒํท์ผ๋ก ๋ค์ด๋ ํธ ๋๊ธฐํ(
- ์๋ํ๋ SemVer ๋ฐ Release ์์ฑ
- ์ปค๋ฐ ๋ฉ์์ง ํค๋ ํ ํฐ(
feat:,fix:) ๊ท๊ฒฉ์ ๊ธฐ๊ณ์ ์ผ๋ก ํ์ฑํ์ฌ Semantic Versioning ๋ฒ์ ์ ์๋ ๊ฐฑ์ - ๋ณ๊ฒฝ ์ด๋ ฅ(Changelog) ์์ฑ ๋ฐ ๋ฆด๋ฆฌ์ฆ ๋ฐํ ๊ณผ์ ์ 100% ์๋ํํ์ฌ ๋ฐฐํฌ ์ ๋ขฐ์ฑ๊ณผ ๋ณ๊ฒฝ ์ถ์ ๊ฐ๋ ์ฑ ํ๋
- ์ปค๋ฐ ๋ฉ์์ง ํค๋ ํ ํฐ(
์ฝ๋ ํตํฉ ์์ ๋ถํฐ ํ๋ก๋์ ๋ฐฐํฌ ์๋ฃ ์์ ๊น์ง ์์คํ ์ ์์ ์ฑ๊ณผ ์ ํฉ์ฑ์ ์ค์๊ฐ ์ ์ฆํ๋ ํ์ง ๊ฒ์ฆ ๋ฐ ์น์ธ ๊ฒ์ดํธ ํต์ ์ฒด๊ณ์ ๋๋ค.
- ๋ค๋จ๊ณ ์ฝ๋ ์ ํฉ์ฑ ๊ฒ์ฆ
- ์ปดํ์ผ & ๋จ์ ํ ์คํธ: ๋ก์ปฌ PR ์์ฑ ์์ ๋ฐ Actions ํ์ดํ๋ผ์ธ์์ Spring Boot ๋จ์ ํ ์คํธ(Gradle) ๋ฐ Vue ๋จ์ ํ ์คํธ(Vitest)๋ฅผ ์๋ ๊ฒ์ฆ
- Ansible Lint ์ ์ ๊ฒ์ฌ: ์ธํ๋ผ ๋ณ๊ฒฝ ์ ํ๋ ์ด๋ถ์ ๋ฌธ๋ฒ ๊ท๊ฒฉ ์ด๊ธ๋จ์ ์ปดํ์ผ ์ ์ ์๋ ์ง๋จํด ์ค์ ๊ฒฐํจ ์ฌ์ ์ฐจ๋จ
- Playwright E2E ๋ธ๋ผ์ฐ์ ํ
์คํธ
- Staging ์๋ฒ ๋ฐฐํฌ ์๋ฃ ์ฆ์ ์ค์ ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ (
frontend/e2e/staging.spec.ts)๋ฅผ ๊ฐ๋ํ์ฌ ํ ํ๋ฉด ๋ก๋ฉ, ๋ ธ๋ ธ๊ทธ๋จ ์บ๋ฒ์ค ํด๋ฆญ/์์น ๋ฐ ์ต๋ช ๊ฐ์ ๋ก์ง์ ์ ์ ๊ด์ ์์ ์๋ ์ ๊ฒํ์ฌ ํ์ง ๊ฒฐํจ ์ ์ ์๋ฐฉ
- Staging ์๋ฒ ๋ฐฐํฌ ์๋ฃ ์ฆ์ ์ค์ ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ (
- ์๋ ์น์ธ ๋ฐฐํฌ ํต์ (Manual Gate)
- Staging ํ๊ฒฝ์์ ์ ๋/E2E ํ ์คํธ๊ฐ 100% ํฉ๊ฒฉํ๋ฉด ๋ฐฐํฌ ์ํฌํ๋ก์ฐ๋ฅผ ์ผ์ ์ ์ง์ํค๊ณ , ๊ด๋ฆฌ์๊ฐ ์ง์ GitHub Environment ์น์ธ ์ฝ์์์ ๋ฆด๋ฆฌ์ฆ ์์ ์ฑ์ ๊ฒํ /์น์ธํด์ผ๋ง Production ํ๊ฒฝ์ผ๋ก ์น๊ฒฉ ๋ฐฐํฌ๋๋๋ก ์ค๊ณํ์ฌ ์ค๋ฐฐํฌ ๋ฆฌ์คํฌ ์ฐจ๋จ
- ๋ฐฐ๊ฒฝ
- Staging๊ณผ Production ์ธํ๋ผ ์ค์ ์ด ๋์ผ Terraform ์ฝ๋์ ๋ฌถ์ฌ ์ผ๊ด ๋ฐ์๋๋ ์ค, ์ด์ ํ๊ฒฝ S3 ๋ฒํท์ ์ ์ ์์ฐ์ด ์๋ฉ๋์ง ์์ ์ํ์์ DNS A ๋ ์ฝ๋๊ฐ CloudFront/S3๋ก ๋จผ์ ์ค์์นญ๋์ด ์ด์ ์ ์ฒด ์ ์ ์ฐจ๋จ(
AccessDenied) ์ฅ์ ๋ฐ์ (Access Failure Report). - ํซํฝ์ค ๋์ค GitHub Actions์
cancel-in-progress: true์ค์ ์ผ๋ก ์ธํด Nginx ์ธ์ฆ์ ๋ฐ๊ธ ํ๋ก์ธ์ค ๋์ค ํ์ ์ปค๋ฐ์ด ์ด์ ๋น๋๋ฅผ ๊ฐ์ ์ทจ์ํ๋ฉด์ ์ค์๋ฒ SSL ์ธ์ฆ์ ์ ์ค๋ก ์ธํ HTTPS API ํต์ ๋ถ๋ฅ ์ฅ์ ๋ฐ์ (Handshake Failure Report).
- Staging๊ณผ Production ์ธํ๋ผ ์ค์ ์ด ๋์ผ Terraform ์ฝ๋์ ๋ฌถ์ฌ ์ผ๊ด ๋ฐ์๋๋ ์ค, ์ด์ ํ๊ฒฝ S3 ๋ฒํท์ ์ ์ ์์ฐ์ด ์๋ฉ๋์ง ์์ ์ํ์์ DNS A ๋ ์ฝ๋๊ฐ CloudFront/S3๋ก ๋จผ์ ์ค์์นญ๋์ด ์ด์ ์ ์ฒด ์ ์ ์ฐจ๋จ(
- ํด๊ฒฐ ๋ฐฉ์
- ์ธํ๋ผ ํ๊ฒฝ ๋ฌผ๋ฆฌ ๊ฒฉ๋ฆฌ
Terraform Workspace ๋ฐ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๋ฅผ Staging๊ณผ Production์ผ๋ก ์๊ฒฉํ ๋ถํ ํ์ฌ ๋จ์ผ ์คํ์ด ์ค ์ด์๊ณ์ ์ฆ๊ฐ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ก ์กฐ์น. - ์ค์ ๋ฐฐํฌ ๋์์ฑ ์ฐจ๋จ ์ต์
์ ๊ฑฐ
์ค์ ์๋ฒ ์ค์ ๋ฐฐํฌ ๋จ๊ณ(deploy-production)์์cancel-in-progress: false๋ฅผ ๋ช ์ํ์ฌ ์ด์ ์์ ์ด ์ค๋ ํ๊ธฐ๋๋ ์ค์ ์ ํฉ์ฑ ํผ์์ ์์ฒ ์ฐจ๋จ. - ๋ฐฐํฌ ๋จ๊ณ์ ๋์จํ ๊ฒฐํฉ(Loose Coupling)
CloudFront TLS ๋ฐ SSL ์ธ์ฆ์ ๊ต์ฒด ๋ฑ์ ์ํธ ์์กด์ ์ธ ์์ ๋ค์ด ์ค์ ์๋ฒ ์ค๋น ์ํ๋ฅผ ๊ฒ์ฆํ ํ์ ์ด๋ฃจ์ด์ง๋๋ก ์๋ ์น์ธ ๊ฒ์ดํธ(Manual Approval Gate)๋ฅผ ๋์ ํด ์ธํ๋ผ ํ๋ก๋ชจ์ ๋ฐฉ์์ ๊ฐ์ ํจ.
- ์ธํ๋ผ ํ๊ฒฝ ๋ฌผ๋ฆฌ ๊ฒฉ๋ฆฌ
์ฌ์ฉ์๊ฐ ์ธ์ ๋ ์ ์ ํ ์คํ ์ด์ง๋ฅผ ํ๋ ์ดํ ์ ์๋๋ก ์ด๊ฒฝ๋ ์์ฑํ LLM ๋ฐ ๋น๋๊ธฐ ๋ฐฐ์น ์ค์ผ์ค๋ฌ๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ๊ตฌ์ฑํ์ต๋๋ค.
- Gemini ๋น๋๊ธฐ ์์ฑ ์ค์ผ์ค๋ฌ
- ๋งค์ผ ์๋ฒฝ 04:17 ํฌ๋ก ์ค์ผ์ค๋ฌ์ ์ํด
gemini-3.1-flash-liteLLM API๊ฐ ๋น๋๊ธฐ ํธ๋ฆฌ๊ฑฐ๋์ด ์ ๊ท ํผ์ฆ ๋ ์ด์์(๊ทธ๋ฆฌ๋, ์๋ฃจ์ ๋งต)์ ์๋ ์์ฑ - API Rate Limit ์ค๋ฅ ๋ฐฉ์ด๋ฅผ ์ํด ๋ฐฑ์๋ ๋จ์ 5์ด์ ์ง์ฐ ๊ฐ๊ฒฉ(Delay Interval) ๋ฐ 3ํ์ ์ง์ ๋ฐฑ์คํ ์ฌ์๋(Exponential Retry) ์ฅ์น๋ฅผ ํตํฉ ์ค๊ณ
- ๋งค์ผ ์๋ฒฝ 04:17 ํฌ๋ก ์ค์ผ์ค๋ฌ์ ์ํด
- ํผ์ฆ ํ๋ณด ์๋น ๋ฒํผ๋ง (FIFO Buffer)
- ๋งค ๋น๋ ์์ ๋ฐ ์ค์๋ฒ ๊ธฐ๋ ์ ํฌ๊ธฐ๋ณ(5x5, 10x10, 15x15, 20x20 ๋ฑ)๋ก ์ต์ 5๊ฐ ์ด์์ ์๋น ํผ์ฆ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํ์ฌ ๋ฒํผ ํ ์ด๋ธ์ ์ ์ ์ ์ถ(FIFO) ํํ๋ก ์์ ์ ์ฌ
- API ์ฅ์ ์์๋ ๋ค์ดํ์ ์์ด ์์ ์ ์ธ ์ผ ๋จ์ ํผ์ฆ ์ ๋ฐ์ดํธ ์๋น์ค๊ฐ ์ง์๋๋๋ก ์์ ์ฅ์น ๊ตฌ๋น
LLM์ด ์ฐฝ์กฐํ ๋ฌด์์ ํจํด ์ค ๋ ผ๋ฆฌ์ ๋ฌด๊ฒฐ์ฑ์ด ๊ฒฐ์ฌ๋ ๋ถ๋ ๋ฌธ์ ๋ฅผ ๊ธฐ๊ณ์ ์ผ๋ก ์๋ ํํฐ๋งํ๋ ์ค์๊ฐ ๊ฒ์ฆ ์์คํ ์ ๋๋ค.
- DFS ๋ฐฑํธ๋ํน ๊ธฐ๋ฐ ๋
ผ๋ฆฌ ๊ฒ์ฆ ์์ง (isLogicalOnly)
- ์์ฑ๋ ์๋ฃจ์
๊ทธ๋ฆฌ๋๋ก๋ถํฐ ๊ฐ๋ก/์ธ๋ก ํํธ ์ซ์๋ฅผ ์ญ์ฐํ ๋ค, Java ๋ฐฑ์๋ ๋จ์ ๊ณ ์ฑ๋ฅ ๋
ธ๋
ธ๊ทธ๋จ ๋ถ์ ์๋ฒ ์๊ณ ๋ฆฌ์ฆ(
NonogramSolver)์ ๊ตฌ๋ํ์ฌ ์ถ๋ก - ๋จ์ ์ฐ๊ธฐ(Guessing)๋ ๋ค์ค ํด(Multiple Solutions)๊ฐ ๋์ค๋ ๋น์ ํ ์ถ์ ๋ฅผ ๊ธฐ๊ณ์ ์ผ๋ก ์ฐจ๋จํ๊ณ , ์ํ์ ์ผ๋ก ์ ์ผํ ํด(Unique Solution)๋ง ์กด์ฌํ๋ ๋ฌด๊ฒฐ์ฑ ํต๊ณผ ๋ฐ์ดํฐ๋ง ๊ฒ์ฆ
- ์์ฑ๋ ์๋ฃจ์
๊ทธ๋ฆฌ๋๋ก๋ถํฐ ๊ฐ๋ก/์ธ๋ก ํํธ ์ซ์๋ฅผ ์ญ์ฐํ ๋ค, Java ๋ฐฑ์๋ ๋จ์ ๊ณ ์ฑ๋ฅ ๋
ธ๋
ธ๊ทธ๋จ ๋ถ์ ์๋ฒ ์๊ณ ๋ฆฌ์ฆ(
- ๋ฐ์ดํฐ ํํฐ๋ง ๊ฐ๋๋ ์ผ
- ์๋ฒ ๊ฒ์ฆ์ ์คํจํ๊ฑฐ๋ ํด์ ์ ์ผ์ฑ์ด ์ ์ฆ๋์ง ์์ ์์ฑ ๋ฐ์ดํฐ๋ ๋ฒํผ ์ ์ฌ ์ ์ฆ์ ๋ฒ๊ธฐ(Discard) ์ฒ๋ฆฌ
- ์ด๋ฅผ ํตํด LLM ํน์ ์ ํ ๋ฃจ์๋ค์ด์ (๋น์ ํ ๋ฐ ๋ฌธ๋ฒ ์ค๋ฅ ๋ฐ์ดํฐ ์ถ๋ ฅ)์ด ์ต์ข ์ฌ์ฉ์ ๊ฒฝํ์ ์ ์ ๋ ๊ฒฝ๋ก๋ฅผ ์์ ๊ฒฉ๋ฆฌ
์ธ๊ฐ์ด ๋ฃจํ์ ์ฐธ์ฌ(HITL)ํ์ฌ ์ธ๊ณต์ง๋ฅ์ด ์์ฑํ ์คํ ์ด์ง์ ํํ์ ์์งํ๊ณ ์ง์ ๊ฐ๋ฅํ ํ์ง์ ๊ฐ๋ ํ๋ ํต์ ์ฅ์น์ ๋๋ค.
-
์ฌ์ฉ์ ํผ๋๋ฐฑ ๋ฃจํ (HITL Feedback)
- ์ค์ ํ๋ ์ด์ด๋ค์ด ๊ฒ์ ํด๋ฆฌ์ด ์ ๋ถ์ฌํ๋ ์ถ์ฒ/๋น์ถ์ฒ(๐/๐) ํ์ ์นด๋ ๋ฐ์ดํฐ๊ฐ ๋ฐฑ์๋ DB
stagesํ ์ด๋ธ์ ์ค์๊ฐ ์ง๊ณ - ํ์ ๋น์จ์ ๊ฐ์์ ์ผ๋ก ์๋ณํ์ฌ LLM ์์ฑ ํ๋กฌํํธ ๋งค๊ฐ๋ณ์ ๋ฐ ์ถ์ ๊ฒฝํฅ ์กฐ์ ์ ๊ธฐ์ด ์์ฐ์ผ๋ก ํ์ฉ
- ์ค์ ํ๋ ์ด์ด๋ค์ด ๊ฒ์ ํด๋ฆฌ์ด ์ ๋ถ์ฌํ๋ ์ถ์ฒ/๋น์ถ์ฒ(๐/๐) ํ์ ์นด๋ ๋ฐ์ดํฐ๊ฐ ๋ฐฑ์๋ DB
-
๋ฐฑ์คํผ์ค ๊ธฐ๋ฐ ์ฐ์ ํ๋ ๋๋ฆฌํธ
- ํ์ ์ด ๋ถ๋ํ๊ฑฐ๋ ๊ธฐํ์ ์ธ ํจํด์ผ๋ก ํ์ ๋ ์คํ ์ด์ง๋ฅผ ๊ด๋ฆฌ์๊ฐ ์๋ ์๋ณ ์ ๋ฐฑ์คํผ์ค ์ด๋๋ฏผ ๋์๋ณด๋ ์์์ ๋จ ํ ๋ฒ์ ์กฐ์์ผ๋ก DB์์ ์ฆ๊ฐ ํ๋ ๋๋ฆฌํธ(Hard Delete)ํ ์ ์๋ ๊ด๋ฆฌ ํ๋ก์ธ์ค ์๋ฆฝ
-
AI ํ์ ๊ฐ๋ฐ ์คํ (AI-assisted Engineering Stack)
๋ณธ ํ๋ก์ ํธ๋ ๋ชจ๋ AI ์์ด์ ํธ ๊ฐ๋ฐ ํ์ ํจ๋ฌ๋ค์์ ์ค์ํ์ฌ, Antigravity IDE ํ๊ฒฝ์์ ์ ๊ณต๋๋ AI ์ฝ๋ฉ ์์ด์ ํธ์์ ์ ๊ตํ ํ์ด ํ๋ก๊ทธ๋๋ฐ(Pair Programming) ๋ฐ ์ฝ๋ ์๋ ๋ฆฌํฉํ ๋ง์ ํตํด ๊ฐ๋ฐ ์์ฐ์ฑ์ ๊ทน๋ํํ์ต๋๋ค.- Gemini 3.5 Flash (Medium):
- IDE ๋ด ๊ธฐ๋: ์ฃผ ๊ฐ๋ฐ ํ๋ก์ธ์ค ๊ธฐ๋, ๋ฐ๋ณต์ ์ธ ์ฝ๋ ๊ตฌํ(Boilerplate ์์ฑ ๋ฑ) ๋ฐ ๋งํฌ๋ค์ด ๋ฌธ์ ์ ๊ทํ ๋ฑ์ ์ง์ฐ ์ต์ํ ์์ ์ ์์ ๊ฐ๋
- ํฌ๋กฌ(Chrome) ์ฐ๋ ๊ธฐ๋: ์น ๋ธ๋ผ์ฐ์ ์ฐ๋ ์ด์์คํดํธ ํ๊ฒฝ์ ํตํด AWS CloudWatch/Grafana ์น ์ฝ์ ์์ ์ค์๊ฐ ์ธํ๋ผ ๋ก๊ทธ ๋ถ์, Chrome DevTools ๊ฒฐํฉํ ํ๋ก ํธ์๋ Canvas ๊ฒฉ์ ๋์์ธ ๋ฐ CSS ๋ ์ด์์ ์ ๊ฒ, ์น ์ฝ์ ๋๋ฒ๊น ์์ ์ ์ ๊ธฐ์ ์ผ๋ก ๋ค๊ฐ ๊ฐ๋
- Claude Sonnet 4.6 (Thinking): ์์ฑ๋ ์์ค์ฝ๋์ ์๋ฏธ๋ก ์ ๋ถ์, ์ปดํ์ผ ๋จ์ ํ ์คํธ ์ ์ ์ง๋จ ํผ๋๋ฐฑ ๋์ ๋ฐ ์ต์ปค ๋งํฌ ๋ฌด๊ฒฐ์ฑ ํฌ๋ก์ค ์ฒดํฌ ๋ฑ ๊ณ ๋ฐ๋ ๋ฆฌ๋ทฐ/๊ฒํ ์์ ์ ๊ธฐ๋
- Claude Opus 4.6 (Thinking): ํ ํฐ ์ฌ์ฉ๋ ๋ฐ ๊ฐ์ฑ๋น(Token Cost Trade-off) ์ ์ฝ์ ๊ณ ๋ คํ์ฌ ์ผ๋ฐ ๊ฐ๋ฐ ๋จ๊ณ์์์ ์์ ์ฌ์ฉ์ ์ ํํ๊ณ , ์ ์ฒด ์ธํ๋ผ ์ํคํ ์ฒ ์ ํฉ์ฑ ์ต์ข ์ ๊ฒ ๋ฐ ํฌํธํด๋ฆฌ์ค ์ ๋ฐ์ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ ๋ฑ ์ต์์ ๋ฆฌ๋ทฐ ๊ฒ์ดํธ ์ญํ ์ ํํด ๊ทนํ ์ ํ์ ์ผ๋ก ์ ๋ณ ๊ฐ๋
- Gemini 3.5 Flash (Medium):
-
์์ด์ ํธ ๊ฑฐ๋ฒ๋์ค ๊ท์น (.agents/rules/)
AI ์ฝ๋ฉ ์์ด์ ํธ์ ํ์ ํ์ฌ ์ง์ ๊ฐ๋ฅํ ๋ฆฌ์คํฌ ๊ด๋ฆฌ ๋ฐ ๊ณ ์ ๋ขฐ์ฑ ์ฝ๋ฉ ์ปจ๋ฒค์ ์ ์ค์ํ๊ธฐ ์ํด ์ ์๋ ํ์ผ ๋ชฉ๋ก์ ๋๋ค. ์ธ๊ฐ ๊ฐ๋ฐ์๊ฐ ์ต์ข ๊ฒํ /์น์ธํ๋ ํต์ ๊ตฌ์กฐ ํ์ ๊ฐ๋ฐ ์ ํฉ์ฑ์ ์ ์งํฉ๋๋ค:๊ท์น ํ์ผ ์ฃผ์ ๊ด๋ฆฌ ๋ชฉ์ ๋ฐ ์ ์ฑ ์์ฝ ํ์ ์ถ์ ์ฌ๋ถ architecture-and-tech-stack.md ํ๋ก ํธ/๋ฐฑ์๋/์ธํ๋ผ ๋ ์ด์ด์ ๋ค์ค ๋์ ์์ ์ฐจ๋จ, Vue Reactivity ๋ ผ๋ฆฌ ์ ์ถ ๋ฐฉ์ง, ์์ฐจ ๋ฐฐํฌ ์ค์ Git Trackeddocumentation-guidelines.md ์๋๊ฒฝ๋ก(file:// ๊ธ์ง) ์ฌ์ฉ, ๋งํฌ๋ค์ด ๊ฐํ ๊ท์ ์ค์, ๋น๊ต ์์น ๋ฐ์ดํฐ ๊ธฐ์ ์ ํ ์ด๋ธ(Table) ์๊ฐํ ์๋ฌดํ Git Trackedgit-and-commit-guidelines.md Conventional Commits ๊ท์น ์ค์, ๋ก์ปฌ ์ปค๋ฐ ์๋ ๋ณด์กด ๋ฐ ์๊ฒฉ push ๊ฐ๋ฐ์ ์์ Git Tracked (Force Added)workflow-and-tdd.md ์ฝ์ด ๋ก์ง ์์ฑ ์ TDD(Test-Driven Development) ์ ํ ์๋ฌดํ ๋ฐ progress_state.md ์์ ๋๊ธฐํ Git Trackedsafety-and-communication.md ์๊ตฌ์ฌํญ์ด ๋ชจํธํ ๊ฒฝ์ฐ ์์ ๊ตฌํ(No Guessing)์ ์ค๋จํ๊ณ ๊ฐ๋ฐ์ ์น์ธ ๋๊ธฐ Git Trackedincident-reporting.md ์ฅ์ ๋ฆฌํฌํธ ์์ฑ ์ 3W1H ์ฌ์์ ๊ทผ๊ฑฐํ ๊ตฌ์ฒด์ ์์ธ-๊ฒฐ๊ณผ ์์น ๋ช ์ธ ๋ฐ ํฌ์คํธ๋ชจํ ๊ตฌ์กฐํ Git Tracked
- ๋ฐฐ๊ฒฝ
- ์ด๊ฒฝ๋ LLM ๋ชจ๋ธ์ด 30x30 ๋ํ ๊ทธ๋ฆฌ๋ ์์ฑ ์ ์๋ต ์ง์ฐ์ ์๋ผ๊ธฐ ์ํด JSON ํฌ๋งท ๋์
Array(30).fill(0)๊ฐ์ JS ๋ฌธ๋ฒ์ ๋ณํ ๋ฐํํ์ฌ ๋ฐฑ์๋ Jackson ์ญ์ง๋ ฌํ ์ค๋ฅ(JsonParseException) ๋ฐ ๋ฐฐ์น ์ค์ผ์ค๋ฌ ์ค๋จ ์ฅ์ ๋ฐ์ (Daily Puzzle Failure Report).
- ์ด๊ฒฝ๋ LLM ๋ชจ๋ธ์ด 30x30 ๋ํ ๊ทธ๋ฆฌ๋ ์์ฑ ์ ์๋ต ์ง์ฐ์ ์๋ผ๊ธฐ ์ํด JSON ํฌ๋งท ๋์
- ํด๊ฒฐ ๋ฐฉ์
- AI ํ๋กฌํํธ์
MUST be a literal 2D JSON array์ ์ฝ ๊ฐ๋๋ ์ผ์ ์ฃผ์ ํ๊ณ , ๋ํ ํผ์ฆ ์์ฑ ์ ์ถ๋ ฅ ํ ํฐ ์์ ์ฑ ํ๋ณด๋ฅผ ์ํด ํ๋ณด๊ตฐ(Candidate) ๊ฐ์๋ฅผ 5๊ฐ์์ 2๊ฐ๋ก ์ถ์ ์กฐ์ ํ์ฌ ํ์ฑ ์ ๋ขฐ์ฑ์ 100%๋ก ํ๋ณดํจ.
- AI ํ๋กฌํํธ์
์ด๊ฒฝ๋ ์ธํ๋ผ ์์์ ๋ฐํ์ผ๋ก ๊ตฌ์ถ๋ ์๋น์ค์ ์ฌ๋ฌด์ ๋น์ฉ ํจ์จ์ฑ๊ณผ ์์คํ ์ ๋ขฐ์ฑ(Reliability) ๋ฐ ์ด์ฉ์ ์งํ ์ค์ธก ๊ฒฐ๊ณผ๋ฅผ ๋์กฐ ๋ถ์ํ์ฌ ๊ธฐ์ ์์ฌ๊ฒฐ์ ์ ํ๋น์ฑ์ ๊ฒ์ฆํฉ๋๋ค.
-
์ธํ๋ผ ์๊ฐ ์ด์ ๋น์ฉ ๋ถ์ (Monthly Billing Summary)
์์ ๋ค์คํ ๋ฐ ๊ด๋ฆฌํ DB ๋ฐฐ์ ๋ฑ์ผ๋ก ๊ธฐ์กด ์์ ์ด์๋น ๋๋น ์ฝ 80%์ ๋น์ฉ ์ ๊ฐ์ ์ ์งํ๊ณ ์์ต๋๋ค.๊ตฌ๋ถ (Category) ๊ธฐ์กด ๊ตฌ์ฑ ์์ ๋น์ฉ (Estimated) ์ต์ ํ ๊ตฌ์ฑ ์ค์ ๋น์ฉ (2026๋ 6์) ์ฃผ์ ๋น๊ณ (Key Notes) ์ปดํจํ ๋ฐ ์คํ ๋ฆฌ์ง $20.00 / ์ (t3.micro) $5.50 / ์ (t3a.nano + EBS) GraalVM ๋ค์ดํฐ๋ธ ์ปจํ ์ด๋ํ๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ ์ค๋ ์ฑ ๊ทน๋ณต ๋ก๋ ๋ฐธ๋ฐ์ $20.00 / ์ (AWS ALB) $0.00 / ์ (Self-hosted Nginx) ALB ์ ๊ฑฐ ํ Route 53 ๊ณ ์ EIP ๋ค์ด๋ ํธ ๋งคํ ๋ฐ์ดํฐ๋ฒ ์ด์ค $15.00 / ์ (RDS PostgreSQL) $0.00 / ์ (PostgreSQL Container) EC2 ํธ์คํธ ๋ด๋ถ Docker Compose ํ๊ฒฝ ๊ฐ๋ ๋คํธ์ํฌ & ๋๋ฉ์ธ N/A *1 $4.74 / ์ (IP ์ฃผ์ + Route 53) ํผ๋ธ๋ฆญ IPv4 ์ฌ์ฉ๋ฃ ($3.70) + ํธ์คํ ์์ญ ($1.04) ๊ธฐํ (๋ฐ์ดํฐ ์ ์ก ๋ฑ) N/A *1 $1.21 / ์ ๋ฐ์ดํฐ ํธ๋ํฝ ์ ์ก ๋ฐ ์ ํธ๋ฆฌํฐ ์์ ๋น์ฉ ํฉ๊ณ (Total) ์ฝ $55.00 / ์ ์ด $11.45 / ์ ๊ธฐ์กด ๋๋น ์ฝ 80% ๋น์ฉ ์ ๊ฐ ๋ฌ์ฑ (์ธํ ์ค์ฒญ๊ตฌ์ก) *1: ๊ธฐ์กด ๊ตฌ์ฑ ๋จ๊ณ์์ ์ฐ์ถ๋์ง ์์ ๋คํธ์ํฌ ์ ์ง ๋ฐ ๋๋ฉ์ธ ๊ณ ์ ๋น์ฉ์ ๋๋ค.
-
์๋น์ค ์์ค ๋ฐ ์ ๋ขฐ๋ ๋น๊ต ๋ถ์ (Reliability Performance Dashboard)
์ต๊ทผ 7์ผ(6์ 25์ผ ~ 7์ 2์ผ)๊ฐ ์ต์ ํ ํ๋์ด ์์ ํ ์ข ๊ฒฐ๋์ด ์์ ๊ถค๋์ ์ง์ ํ ์์ ์ Grafana Cloud ์ค์ธก ๋ฐ์ดํฐ ๊ธฐ๋ฐ ๋์กฐ ๋ถ์์ ๋๋ค. ๊ทน๋จ์ ์ธ 512MB RAM ์์ ์ ์ฝ์ ๊ทน๋ณตํ๊ณ ์์ฉ ๊ฐ์ฉ์ฑ ๋ชฉํ๋ฅผ ์๋ฒฝํ ์ถฉ์กฑํ๊ณ ์์์ ์ฆ๋ช ํฉ๋๋ค.์๋น์ค ์์ค ์งํ (SLI) ๋ชฉํ ํ๊ณ์น (SLO Target) ์ค์ธก ์ฑ๊ณผ (7์ผ ํ๊ท ์ค์ธก์น) ์ฃผ์ ๋ถ์ ๋ฐ ์ค๊ณ ๊ทผ๊ฑฐ (Design Rationale) Availability (๊ฐ์ฉ์ฑ) 99.0% 98.6% ~ 98.7%
(ํ๊ท 98.63%)์ด๊ธฐ ์์ ๊ณ ๊ฐ(OOM) ๋ฌธ์ ๋ฅผ ๊ทน๋ณตํ ๋ค ์์ฉ ์์ค(99.0% ์๊ณ)์ ๊ทผ์ ํ ์์ ์ฑ ํ๋ณด MTBF (ํ๊ท ๊ณ ์ฅ ๊ฐ๊ฒฉ) 720์๊ฐ (30์ผ) ์ด์ 10.4 ~ 18.4 ์๊ฐ
(ํ๊ท 14.2 ์๊ฐ)์ปจํ ์ด๋ ๊ฒฝ๋ํ ๋ฐ SWAP ํ์ฑํ๋ก ๋ฐฐํฌ ์ฃผ๊ธฐ ์์ ํ ๋ฐ ๋น์ ์ ์ค์ง ์๋ฐฉ MTTR (ํ๊ท ๋ณต๊ตฌ ์๊ฐ) 10๋ถ ์ด๋ด 9.0 ~ 14.9 ๋ถ
(ํ๊ท 11.76 ๋ถ)GraalVM Native Image ์ด๊ณ ์ ์ปจํ ์ด๋ ๊ฐ๋ ๋ฐ ๊ฒฝ๋ณด ์ฐ๋์ ํตํ ๋ณต๊ตฌ ๋์ ๋จ์ถ RPO (๋ณต๊ตฌ ์์ ๋ชฉํ) ์ต๋ 6์๊ฐ ์ต๋ 6์๊ฐ (๋ฐ์ดํฐ ์ค์ ์ค 0๊ฑด) ์ผ 4ํ DB Dump ํ์ผ Amazon S3 ์๊ฒฉ ์์ฐ ์ค์ผ์ค ๊ฐ๋ RTO (๋ณต๊ตฌ ์๊ฐ ๋ชฉํ) ์ต๋ 20๋ถ 3๋ถ ์ด๋ด (๋ณต์ ์๋ํ ํ ์คํธ ๊ฒฐ๊ณผ) Terraform/Ansible ์ฝ๋๋ฅผ ํตํ ์ํด๋ฆญ ์ฌ๋น๋ ๋ฐ ๋คํ ์๋ ์ ์ฌ -
์ค์ธก ์งํ์ ๋ํ ๊ธฐ์ ํ๊ณ (Operational Metrics Retrospective)
- ๊ฐ์ฉ์ฑ ์ ํ ์์ธ ๋ถ์: ํ๋ก์ ํธ ์ด๊ธฐ t3a.nano(512MB RAM)์ ๊ทน๋จ์ ์ธ ์์ ์ ์ฝ ํ์์ Nginx/Spring/PostgreSQL์ ๋์ ๊ตฌ๋ํ ๋์ OOM(Out of Memory) ํ์๊ณผ Docker ๋ ์ด์ด๋ฅผ ํตํ ๋์คํฌ ๊ณ ๊ฐ์ด ์ฃผ ์ฅ์ ์์ธ์ผ๋ก ๊ธฐ๋ก๋์์ต๋๋ค.
- ๋ณต๊ตฌ ์๊ฐ(MTTR) ์ง์ฐ: ์ด๊ธฐ ๊ฒฝ๋ณด ์ฑ๋(Slack/Email SNS) ๋ฐ SSM ์ธ์ ๋งค๋์ ๋ฅผ ํตํ ๋ณต๊ตฌ ์๋ํ ์ธํ๋ผ๊ฐ ์์ ํ ๊ตฌ์ถ๋๊ธฐ ์ , ์๋ SSH ์ ์ ๋ฐ ๋ฐ๋ชฌ ๋ถ์ ์ฒ๋ฆฌ์ ๋ง์ ์๊ฐ์ด ์ง์ฐ๋์์ต๋๋ค.
- ์์ ํ ์ฑ๊ณผ: ํธ๋ฌ๋ธ์ํ (1.5.1. Host Memory Exhaustion Incident) ์กฐ์น(Agentless Pull ์ค์์นญ, 30MB ์ดํ GraalVM Native Image ๋ฐฐํฌ, swap ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์ฑ, Docker GC ์คํฌ๋ฆฝํธ ๋ฐ SSM ํฐ๋๋ง ๊ณ ๋ํ)๋ฅผ ์๋ฃํ ์ต๊ทผ 7์ผ ๊ฐ๋ ๊ธฐ์ค์ผ๋ก๋ ํ๊ท ๊ฐ์ฉ์ฑ 98.63% ๋ฐ ํ๊ท MTTR 11.76๋ถ ์์ค์ผ๋ก ์์ ๊ถค๋์ ์์ฐฉํ์ฌ ์ฑ๋ฅ ๊ฐ์ ํจ๊ณผ๋ฅผ ๊ฒ์ฆํ์ต๋๋ค.
-
Grafana Live Service SLA Dashboard
์์ธ ๊ฐ์ฉ์ฑ ๋ฉํธ๋ฆญ, MTTR, MTBF ์ค์๊ฐ ๋ณ๋ ์ถ์ด๋ฅผ ์ฆ๋นํ๋ Grafana Live Service SLA Dashboard Snapshot ์บก์ฒ๋ณธ์ ๋๋ค.
-
๊ตฌ์ถ ์ดํ ์๋น์ค ๋์ ์ค์ธก ์งํ (Google Analytics 4 / Actuator)
- ํ์ฑ ์ฌ์ฉ์ ์ (Active Users): 39๋ช (์ต๊ทผ 7์ผ Google Analytics 4 ์ค์ธก ๊ธฐ์ค)
- ์ด ์ด๋ฒคํธ ์ (Total Events): 535ํ (์ฌ์ฉ์ ์ํธ์์ฉ ๋ฐ ๊ฒ์ ํ๋ ์ด ํ์ ๋ก๊ทธ)
- ์ฌ์ฉ์๋น ํ๊ท ์ฐธ์ฌ ์๊ฐ (Average Engagement Time): 2๋ถ 53์ด (์ฐธ์ฌ ๋ชฐ์ ๋ ํฅ์ ํ์ธ)
- AI ์๋ ์์ฑ ํผ์ฆ ์ (Daily Generated): 60+ ๊ฐ (๋ฐ์ผ๋ฆฌ ์์ฑ๊ธฐ ๋ฐ ๋ฌด๊ฒฐ์ฑ ์๋ฒ ๊ฒ์ฆ ํต๊ณผ ๋ฐ์ดํฐ ๋์ )
-
Google Analytics 4 User Report
์ต๊ทผ 7์ผ๊ฐ์ rogic.io ์ค ์ฌ์ฉ์ ํต๊ณ(ํ์ฑ ์ฌ์ฉ์ 39๋ช , ์ ์ฌ์ฉ์ 37๋ช , ํ๊ท ์ฐธ์ฌ ์๊ฐ 2๋ถ 53์ด)๋ฅผ ์ฆ๋นํ๋ ๊ตฌ๊ธ ์ ๋๋ฆฌํฑ์ค 4 ํ๋ ๋ณด๊ณ ์ ์๋ณธ ์บก์ฒ๋ณธ์ ๋๋ค.
To run rogic.io on your local workstation, select one of the options below:
์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์คํ(Database, Backend, Frontend)์ ํ ๋ฒ์ ๋น๋ํ๊ณ ๊ธฐ๋ํ๋ ค๋ ๊ฒฝ์ฐ ์๋ ์ต์ ์ ์ ํํฉ๋๋ค.
# In the project root, compile, build and start all container services
docker compose up --build- Frontend Web Client:
http://localhost:5173 - Backend REST API:
http://localhost:8080 - Prerequisites: Docker & Docker Compose ์ค์น ํ์.
์ฝ๋ ์์ ์ ์ฆ๊ฐ์ ์ธ ๋ผ์ด๋ธ ๋ฐ์ ๋ฐ ํซ ๋ฆฌ๋ก๋ฉ(Vite dev server)์ ์ํ๋ ๊ฒฝ์ฐ ์๋ ๋จ๊ณ๋ณ๋ก ์๋น์ค๋ฅผ ๊ธฐ๋ํฉ๋๋ค.
-
Step 1: PostgreSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ธฐ๋
# Start only the database container in the background docker compose up -d db -
Step 2: ๋ฐฑ์๋ API ์๋ฒ ์คํ
cd backend ./gradlew bootRun- API Server ๊ตฌ๋ ์ฃผ์:
http://localhost:8080 - Prerequisites: Java 17 JDK ์ค์น ํ์.
- API Server ๊ตฌ๋ ์ฃผ์:
-
Step 3: ํ๋ก ํธ์๋ ํด๋ผ์ด์ธํธ ์คํ
cd frontend npm install npm run dev- Frontend Client ๊ตฌ๋ ์ฃผ์:
http://localhost:5173 - Prerequisites: Node.js 20+ ์ค์น ํ์.
- Frontend Client ๊ตฌ๋ ์ฃผ์:
๋ณด์ ๊ทธ๋ฃน 22๋ฒ ํฌํธ ํ์ ํ๊ฒฝ ํ์์ ์๊ฒฉ EC2 ์ธ์คํด์ค ํฐ๋ฏธ๋์ ์ ์ํ๊ฑฐ๋ Ansible ํฐ๋์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
-
AWS CLI ๋ฐ Session Manager Plugin ์ค์น
๋ก์ปฌ ๊ธฐ๊ธฐ์ AWS CLI๋ฅผ ์ต์ ์ํ๋ก ์ ์งํ๊ณ , SSH ํฐ๋๋ง์ ์ง์ํ๊ธฐ ์ํด AWS ๊ณต์ session-manager-plugin์ ์ค์นํฉ๋๋ค. -
๋ก์ปฌ SSH Config ์ค์ (~/.ssh/config)
๋ณด์ ๊ทธ๋ฃน์์ SSH(22) ํฌํธ๊ฐ ํ์๋์๋๋ผ๋ ํธ์คํธ์ SSM ์์ด์ ํธ๋ฅผ ํ๋ก์๋ก ์ผ์ SSH ํฐ๋์ ์๋ฆฝํ ์ ์๋๋ก ์๋ ์ค์ ์ ๋ก์ปฌ SSH ํ๊ฒฝ ํ์ผ์ ๋ฑ๋กํฉ๋๋ค.# SSH over SSM Tunnel Configuration Host i-* mi-* ProxyCommand aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p -
EC2 Host Connection Command
์ธ์คํด์ค ID์ ๊ธฐ์กด SSH ์ธ์ฆ ํค๋ฅผ ์ฌ์ฉํด 22ํฌํธ ๋ฐฉํ๋ฒฝ ์ฐจ๋จ์ ์ฐํํ์ฌ ์ ์ธ์ ์ ์์ ํ๊ฒ ์๋ฆฝํฉ๋๋ค.ssh -i ~/.ssh/nemologic-key.pem ubuntu@i-xxxxxxxxxxxxxxxxx -
Ansible SSM SSH Tunneling Configuration (hosts.ini)
22๋ฒ ํฌํธ ์ฐจ๋จ ์ํ์์ Ansible Playbook ๊ฐ๋์ ์ํด ํธ์คํธ์ SSM ์์ด์ ํธ๋ฅผ ํ๋ก์ ํฐ๋๋ก ์ผ์ ์ฐ๊ฒฐํ ์ ์๋๋ก ์๋์ ๊ฐ์ดhosts.ini์ค์ ์ ๊ตฌ์ฑํ์ฌ SSH ์ฐ๊ฒฐ์ ์บก์ํํฉ๋๋ค.
[nemologic_servers]
nemologic-app-server ansible_host=<EC2_Instance_ID> ansible_user=ubuntu ansible_ssh_private_key_file=<PEM_File_Path> ansible_ssh_common_args='-o ProxyCommand="aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p"'Note
์์ ๋ด ๊ธฐํธ ์ ์: probe_success)๋ฅผ ์๋ฏธํฉ๋๋ค. ์ด๊ธฐ ์์ง ์์ ์ ๊ฐ์ฉ ์ํ๊ฐ 0(์ฅ์ )์ผ๋ก ์์ํ๋ ๊ฒฝ์ฐ, ์ฒซ ๋ฒ์งธ ๋ณํ(0 โ 1)๊ฐ ์ฅ์ ๋ณต๊ตฌ์์๋ ํ์ ๋ณํ ํ์๊ฐ ๋ฐํ๋์ด ๋๋์
๊ฒฐ๊ณผ์ ์์์ ์ด ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก ์ฟผ๋ฆฌ์์๋ ์ ์ ๋๋์
(๋ด๋ฆผ) ์ฒ๋ฆฌ๋ฅผ ์ ์ฉํฉ๋๋ค.
- API Health Status
sum(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"})
- Dynamic Service Availability
avg_over_time(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) * 100
- Dynamic Incident Count
floor(changes(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) / 2)
- Dynamic MTTR (Mean Time To Recovery)
((count_over_time(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) - sum_over_time(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range])) * 60) / clamp_min(changes(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) / 2, 1)
- Dynamic MTBF (Mean Time Between Failures)
(sum_over_time(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) * 60) / clamp_min(changes(probe_success{job="nemologic-api-health", instance="https://rogic.io/actuator/health"}[$__range]) / 2, 1)
-
$\text{clamp}_{\text{min}}(x, d) = \max(x, d)$ ์ ์๋ฏธํ๋ฉฐ, ์ธก์ ๋์ ๊ธฐ๊ฐ ์ค ์ฅ์ /๋ณต๊ตฌ ์ ํ ์ด๋ฒคํธ๊ฐ 0ํ ๋ฐ์ํ ๊ฒฝ์ฐ ๋ฐ์ํ๋ ๋ถ๋ชจ 0 ์ค๋ฅ(Zero-division) ๋ฐฉ์ง๋ฅผ ์ํด PromQL ํจ์๋ก ๋ณด์ ํ ๊ฒ์ ๋๋ค.



