Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview
EduKit is an AI-powered student record management system for Korean teachers (생활기록부 작성 및 관리 서비스). The project uses a multi-module Spring Boot architecture with Java 21.

## Module Architecture

The project follows a layered architecture with clear separation of concerns:

### 🎯 Module Dependencies
```
edukit-api (REST API Layer)
├── edukit-core (Business Logic)
│ └── edukit-common (Shared Utilities)
└── edukit-external (External Integrations)
├── edukit-core
└── edukit-common
```

### 📦 Module Descriptions

- **edukit-api**: REST API controllers, security config, main application entry point
- Contains: Controllers (v1/v2), security, validation, Swagger/OpenAPI
- Technologies: Spring Security, Spring Boot Actuator, Swagger, Prometheus metrics

- **edukit-core**: Core business logic and domain services
- Contains: JPA entities, repositories, business services, domain logic
- Technologies: Spring Data JPA, QueryDSL, Redis, JWT, Apache POI, Micrometer

- **edukit-external**: External service integrations
- Contains: AI services (OpenAI), AWS services (S3, SES, SQS), Slack integration
- Technologies: Spring AI, AWS SDK, WebFlux, Resilience4j, Thymeleaf

- **edukit-common**: Shared utilities and constants

## Build System & Common Commands

### 🔧 Build Commands
```bash
# Build entire project
./gradlew build

# Build specific module
./gradlew :edukit-api:build

# Run tests
./gradlew test

# Run specific module tests
./gradlew :edukit-core:test

# Clean build
./gradlew clean build

# Generate bootJar (executable)
./gradlew bootJar

# Run application locally
./gradlew bootRun
```

### 🐳 Docker Commands
```bash
# Development environment
docker-compose -f docker-compose.dev.yml up

# Production environment
docker-compose -f docker-compose.prod.yml up
```

## Key Technologies & Integrations

### 🗄️ Database & Storage
- **MySQL**: Primary database with Flyway migrations
- **Redis**: Caching and session management
- **AWS S3**: File storage

### 🤖 AI & External Services
- **Spring AI**: OpenAI integration for student record generation
- **AWS SES**: Email notifications
- **AWS SQS**: Message queuing
- **Slack**: Integration for notifications

### 📊 Monitoring & Observability
- **JMX + Prometheus**: Application metrics collection
- **Micrometer**: Metrics instrumentation
- **Spring Boot Actuator**: Health checks and monitoring endpoints
- **JMX Config**: Custom Tomcat and JVM metrics (see `jmx-config.yml`)

## Environment Configuration

### 🌍 Profiles
- `local`: Local development
- `dev`: Development environment (AWS RDS/Redis)
- `prod`: Production environment

### 📁 Configuration Files
- `application-{profile}.yml`: Profile-specific configurations
- Environment variables managed via `.env` files
- JMX monitoring configured in `jmx-config.yml`

## Domain Structure

### 📚 Core Domains
- **Student**: Student management and information
- **StudentRecord**: AI-powered student record generation and management
- **Member**: User/teacher management and authentication
- **Auth**: JWT-based authentication (v1 and v2 APIs)
- **Notice**: Announcements and notifications
- **Admin**: Administrative functions

### 🏗️ Package Structure Pattern
```
com.edukit.{domain}
├── controller/ # REST endpoints
├── service/ # Business logic
├── repository/ # Data access
├── entity/ # JPA entities
├── dto/ # Data transfer objects
└── exception/ # Domain-specific exceptions
```

## Testing Strategy

### 🧪 Test Structure
- Unit tests: `src/test/java`
- Integration tests: Available in all modules
- Test profile: `application-test.yml`

## Development Guidelines

### 🔐 Security Considerations
- JWT tokens for authentication
- AWS credentials managed via environment variables
- Database credentials in profile-specific configs
- Never commit `.env` files or sensitive data

### 🎯 Jira Integration
- Branch naming: `feat/EDMT-{ticket-number}`
- Commit format: `[feat/refac/fix] description`
- PR template includes Jira ticket references (Korean descriptions preferred)

### 🚀 Deployment
- **Blue-Green Deployment**: Configured in Docker Compose
- **Monitoring Ports**: 8081 (blue), 8082 (green) for JMX metrics
- **Health Checks**: Available via Spring Boot Actuator
- **Log Management**: Structured logging with Logback + Logstash encoder
Comment thread
wldks1008 marked this conversation as resolved.

## Common Development Patterns

### 🔄 Metrics Collection
- Custom metrics using `@Aspect` and Micrometer
- Student record generation tracking
- Performance monitoring for AI operations

### 🌐 API Versioning
- v1 and v2 controller patterns
- Backward compatibility considerations

### 📝 File Processing
- Excel file handling with Apache POI
- File upload/download via AWS S3

This architecture supports scalable AI-powered educational services with robust monitoring, security, and Korean localization support.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,6 @@ Temporary Items

### log file ###
*.log

### JMX Prometheus Agent ###
jmx_prometheus_javaagent*.jar
8 changes: 8 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ services:
PROFILE: dev
TZ: Asia/Seoul
HOSTNAME: ${HOSTNAME_BLUE:-dev-1-blue}
JAVA_OPTS: "-javaagent:/app/jmx_prometheus_javaagent.jar=8081:/app/jmx-config.yml"
ports:
- "8080:8080"
- "8081:8081"
Comment thread
wldks1008 marked this conversation as resolved.
env_file:
- .env
restart: always
volumes:
- ./log/blue:/log
- /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
- ./jmx-config.yml:/app/jmx-config.yml
networks:
- app-network

Expand All @@ -23,13 +27,17 @@ services:
PROFILE: dev
TZ: Asia/Seoul
HOSTNAME: ${HOSTNAME_GREEN:-dev-1-green}
JAVA_OPTS: "-javaagent:/app/jmx_prometheus_javaagent.jar=8082:/app/jmx-config.yml"
ports:
- "8081:8080"
- "8082:8082"
env_file:
- .env
restart: always
volumes:
- ./log/green:/log
- /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
- ./jmx-config.yml:/app/jmx-config.yml
networks:
- app-network

Expand Down
8 changes: 8 additions & 0 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ services:
PROFILE: prod
TZ: Asia/Seoul
HOSTNAME: ${HOSTNAME_BLUE:-prod-1-blue}
JAVA_OPTS: "-javaagent:/app/jmx_prometheus_javaagent.jar=8081:/app/jmx-config.yml"
ports:
- "8080:8080"
- "8081:8081"
Comment thread
wldks1008 marked this conversation as resolved.
env_file:
- .env
restart: always
volumes:
- ./log/blue:/log
- /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
- ./jmx-config.yml:/app/jmx-config.yml
networks:
- app-network

Expand All @@ -23,13 +27,17 @@ services:
PROFILE: prod
TZ: Asia/Seoul
HOSTNAME: ${HOSTNAME_GREEN:-prod-1-green}
JAVA_OPTS: "-javaagent:/app/jmx_prometheus_javaagent.jar=8082:/app/jmx-config.yml"
ports:
- "8081:8080"
- "8082:8082"
env_file:
- .env
restart: always
volumes:
- ./log/green:/log
- /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
- ./jmx-config.yml:/app/jmx-config.yml
networks:
- app-network

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.edukit.common.annotation.MemberId;
import com.edukit.core.studentrecord.exception.StudentRecordErrorCode;
import com.edukit.core.studentrecord.exception.StudentRecordException;
import com.edukit.core.studentrecord.service.StudentRecordService;
import com.edukit.studentrecord.controller.request.StudentRecordPromptRequest;
import com.edukit.studentrecord.facade.StudentRecordAIFacade;
import com.edukit.studentrecord.facade.response.StudentRecordTaskResponse;
Expand All @@ -26,7 +25,6 @@
public class StudentRecordAIController implements StudentRecordAIApi {

private final StudentRecordAIFacade studentRecordAIFacade;
private final StudentRecordService studentRecordService;

@PostMapping("/ai-generate/{recordId}")
public ResponseEntity<EdukitResponse<StudentRecordTaskResponse>> aiGenerateStudentRecord(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.edukit.core.studentrecord.service;
package com.edukit.core.studentrecord.metric;

import java.util.Collections;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.edukit.core.studentrecord.aop;
package com.edukit.core.studentrecord.metric;

import com.edukit.core.studentrecord.db.entity.StudentRecord;
import com.edukit.core.studentrecord.db.enums.StudentRecordType;
import com.edukit.core.studentrecord.service.RecordGenerationTracker;
import com.edukit.core.studentrecord.service.StudentRecordMetricsCounter;
import com.edukit.core.studentrecord.service.StudentRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.edukit.core.studentrecord.service;
package com.edukit.core.studentrecord.metric;

import com.edukit.core.studentrecord.db.enums.StudentRecordType;
import io.micrometer.core.instrument.MeterRegistry;
Expand Down
72 changes: 72 additions & 0 deletions jmx-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
rules:
# Tomcat Thread Pool GAUGE metrics (current state)
- pattern: 'Catalina<type=ThreadPool, name="([^"]+)"><>(currentThreadCount|currentThreadsBusy|maxThreads): (\d+)'
name: tomcat_threads_$2
labels:
port: "$1"
help: Tomcat thread pool current state metrics
type: GAUGE

# Tomcat Thread Pool COUNTER metrics (cumulative)
- pattern: 'Catalina<type=ThreadPool, name="([^"]+)"><>(connectionCount): (\d+)'
name: tomcat_threads_$2_total
labels:
port: "$1"
help: Tomcat thread pool cumulative metrics
type: COUNTER

# Tomcat Connector metrics
- pattern: 'Catalina<type=GlobalRequestProcessor, name="([^"]+)"><>(requestCount|bytesReceived|bytesSent): (\d+)'
name: tomcat_connector_$2_total
labels:
port: "$1"
help: Tomcat connector metrics
type: COUNTER

# JVM Memory metrics
- pattern: 'java.lang<type=Memory><>HeapMemoryUsage.(\w+): (\d+)'
name: jvm_memory_heap_$1_bytes
help: JVM heap memory usage
type: GAUGE

- pattern: 'java.lang<type=Memory><>NonHeapMemoryUsage.(\w+): (\d+)'
name: jvm_memory_nonheap_$1_bytes
help: JVM non-heap memory usage
type: GAUGE

# GC metrics
- pattern: 'java.lang<type=GarbageCollector, name=(.+)><>CollectionCount: (\d+)'
name: jvm_gc_collections_total
labels:
gc: "$1"
help: JVM garbage collection count
type: COUNTER

- pattern: 'java.lang<type=GarbageCollector, name=(.+)><>CollectionTime: (\d+)'
name: jvm_gc_collection_time_milliseconds_total
labels:
gc: "$1"
help: JVM garbage collection time
type: COUNTER

# CPU metrics
- pattern: 'java.lang<type=OperatingSystem><>ProcessCpuLoad: (.+)'
name: jvm_process_cpu_load_ratio
help: JVM process CPU load
type: GAUGE

- pattern: 'java.lang<type=OperatingSystem><>SystemCpuLoad: (.+)'
name: jvm_system_cpu_load_ratio
help: System CPU load
type: GAUGE

# Thread metrics
- pattern: 'java.lang<type=Threading><>ThreadCount: (\d+)'
name: jvm_threads_current
help: Current thread count
type: GAUGE

- pattern: 'java.lang<type=Threading><>DaemonThreadCount: (\d+)'
name: jvm_threads_daemon
help: Daemon thread count
type: GAUGE