Skip to content

[EDMT-453] JMX Prometheus 모니터링 구성 및 학생기록 메트릭스 패키지 구조 개선#71

Merged
wldks1008 merged 7 commits into
developfrom
feat/EDMT-453
Sep 21, 2025
Merged

[EDMT-453] JMX Prometheus 모니터링 구성 및 학생기록 메트릭스 패키지 구조 개선#71
wldks1008 merged 7 commits into
developfrom
feat/EDMT-453

Conversation

@wldks1008

@wldks1008 wldks1008 commented Sep 21, 2025

Copy link
Copy Markdown
Member

📣 Jira Ticket

EDMT-453

👩‍💻 작업 내용

🔍 JMX Prometheus 모니터링 구성

  • JMX Prometheus Java agent를 Docker Compose 파일에 설정
  • Tomcat 및 JVM 메트릭스 수집을 위한 JMX 구성 추가
  • 포트 8081, 8082에서 메트릭스 엔드포인트 노출
  • JMX Prometheus Agent를 .gitignore에 추가

📊 메트릭스 수집 범위

  • Tomcat 메트릭스: 스레드 풀, 커넥터 통계
  • JVM 메트릭스: 힙/비힙 메모리 사용량, GC 통계
  • 시스템 메트릭스: CPU 사용률, 스레드 수

🔧 코드 구조 개선

  • StudentRecordAIController에서 미사용 StudentRecordService 의존성 제거
  • 학생기록 메트릭스 관련 클래스들의 패키지 구조 리팩토링

📝 리뷰 요청 & 논의하고 싶은 내용

  • JMX 구성이 운영 환경에서 성능에 미치는 영향 검토 필요
  • 메트릭스 수집 주기 및 보안 고려사항 검토
  • blue-green 배포 시 메트릭스 연속성 확보 방안

Test Plan

✅ Manual Testing Checklist

  • 개발 환경에서 Docker Compose 정상 실행 확인
  • JMX 메트릭스 엔드포인트 (8081, 8082) 접근 가능 확인
  • Prometheus 형식 메트릭스 데이터 정상 출력 확인
  • Tomcat 스레드 풀 메트릭스 수집 확인
  • JVM 메모리 및 GC 메트릭스 수집 확인
  • 리팩토링된 StudentRecord 관련 기능 정상 동작 확인

🧪 Automated Tests

  • Unit tests pass: ./gradlew test
  • Integration tests pass
  • Build succeeds: ./gradlew build
  • Docker 이미지 빌드 성공 확인

🔍 Code Quality

  • 코드 리뷰 완료
  • 새로운 경고나 오류 없음
  • JMX 구성 보안 검토 완료

Deployment Notes

  • JMX Prometheus Agent JAR 파일이 서버에 설치되어 있는지 확인 (/opt/jmx_prometheus_javaagent.jar)
  • 메트릭스 포트 (8081, 8082) 방화벽 설정 확인
  • 모니터링 시스템(Prometheus)에서 새 엔드포인트 수집 설정 추가
  • blue-green 배포 시 각 인스턴스별 메트릭스 포트 분리 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 신기능
    • 블루/그린 서비스에 Prometheus JMX 에이전트 통합으로 JVM·Tomcat 지표 노출 (블루: 8081, 그린: 8082) 및 기본 JMX 구성 추가.
  • 리팩터링
    • 메트릭 관련 구성 재배치 및 불필요한 변환/종속성 정리로 구조 단순화.
  • 작업
    • JMX 에이전트 JAR 패턴을 .gitignore에 추가하여 버전 관리에서 제외.
  • 문서
    • 리포지토리 작업 가이드를 담은 CLAUDE.md 문서 추가.

@coderabbitai

coderabbitai Bot commented Sep 21, 2025

Copy link
Copy Markdown

Walkthrough

JMX Prometheus 에이전트 통합을 docker-compose(개발/프로덕션)에 추가하고 관련 JMX 설정 파일과 .gitignore 규칙을 추가했습니다. 메트릭 관련 클래스들이 패키지로 이동되었고, 불필요한 컨버터 클래스와 컨트롤러의 서비스 필드가 제거되었습니다.

Changes

Cohort / File(s) Summary of changes
Git ignore update
\.gitignore
jmx_prometheus_javaagent*.jar 무시 규칙 추가.
Docker: JMX agent wiring
docker-compose.dev.yml, docker-compose.prod.yml
JAVA_OPTS에 javaagent 추가, 에이전트 jar 및 jmx-config.yml 마운트, blue:8081 / green:8082 노출.
Metrics package refactor
Moved files:
edukit-core/src/main/java/com/edukit/core/studentrecord/metric/RecordGenerationTracker.java, .../metric/StudentRecordMetricsCounter.java, .../metric/StudentRecordMetricsAspect.java
클래스들의 패키지 선언 변경(예전 ...service/...aop...studentrecord.metric) — 주로 네임스페이스 조정.
Controller cleanup
edukit-api/src/main/java/com/edukit/studentrecord/controller/StudentRecordAIController.java
StudentRecordService 필드 및 import 제거; 메서드 시그니처/로직 변경 없음.
Converter removal
edukit-core/src/main/java/com/edukit/core/common/converter/StudentRecordTypeConverter.java
Spring Converter<String, StudentRecordType> 컴포넌트 파일 삭제.
New JMX config
jmx-config.yml
Tomcat, 커넥터, JVM 메모리/GC/스레드/CPU 등 Prometheus용 JMX metric 규칙 추가.
Docs: Claude guidance
.claude/CLAUDE.md
Claude 코드 작업 가이드 문서 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as 클라이언트
  participant App as 앱 (JVM)
  participant Agent as JMX Prometheus 에이전트
  participant Prom as Prometheus

  Client->>App: HTTP 요청 (blue/green)
  rect rgba(230,240,255,0.5)
    note over App,Agent: 메트릭 노출 추가
    App-->>Agent: JMX MBean 데이터 제공
    Agent-->>Prom: /metrics 응답 (포트 8081/8082)
  end

  Prom->>Agent: GET /metrics
  Agent-->>Prom: jmx-config.yml에 따른 메트릭 텍스트
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

✨feat✨, ✨refac✨

Suggested reviewers

  • TaegeunYou

Poem

새벽 밭을 달리는 토끼 한 마리, 🐇
파랑과 초록 앱에 계측 씨앗 심었네.
자바 에이전트 단추 콩닥, 포트는 8081·8082,
패키지는 정리하고 변환은 덜어 냈지.
프로메테우스가 웃네 — 메트릭이 뛰노는 소리! 📈

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed 제목은 JMX Prometheus 모니터링 구성과 학생기록 메트릭스 패키지 구조 개선이라는 PR의 주요 변경 사항을 명확하고 간결하게 요약하고 있어, 히스토리 스캔 시 핵심을 바로 파악할 수 있습니다.
Description Check ✅ Passed PR 설명에는 Jira 티켓, 작업 내용, 리뷰 요청 및 논의 사항이 템플릿 구조에 맞춰 모두 포함되어 있으며 선택 사항인 스크린샷 섹션을 제외한 필수 항목이 충족되었습니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/EDMT-453

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
edukit-core/src/main/java/com/edukit/core/studentrecord/metric/RecordGenerationTracker.java (1)

16-17: TTL 프로퍼티 기본값과 유효성 보강 필요

@Value 주입 실패(프로퍼티 누락) 시 애플리케이션이 부팅 실패합니다. 운영 안전성을 위해 기본값을 두고, 0/음수일 때 경고 로그를 남겨 동작을 중지하거나 합리적 기본값을 사용하세요.

-    @Value("${record.generation.ttl}")
-    private long ttlSeconds;
+    @Value("${record.generation.ttl:3600}")
+    private long ttlSeconds;
docker-compose.prod.yml (1)

46-46: latest 태그 고정은 배포 재현성/안정성을 해칩니다

프로덕션에서는 구체 버전(가능하면 다이제스트 포함)으로 핀 고정하세요.

-    image: nginx:latest
+    image: nginx:1.27.2
@@
-    image: grafana/promtail:latest
+    image: grafana/promtail:2.9.0

버전은 조직 표준/호환성에 맞춰 조정 바랍니다.

Also applies to: 58-58

edukit-core/src/main/java/com/edukit/core/studentrecord/metric/StudentRecordMetricsAspect.java (2)

25-44: 메트릭 수집 실패가 비즈니스 흐름을 깨뜨릴 수 있음 (예외 전파 위험)

studentRecordService.getRecordDetail 호출이 try-catch 밖에 있어 예외가 상위로 전파됩니다. 메트릭은 베스트‑에포트여야 하므로 전체 블록을 예외로부터 격리하세요.

     @AfterReturning(pointcut = "@annotation(com.edukit.common.annotation.StudentRecordMetrics)")
     public void collectCompletionMetrics(final JoinPoint joinPoint) {
-        Object[] args = joinPoint.getArgs();
-
-        if (args.length == 3) {
-            long memberId = (Long) args[0];
-            long recordId = (Long) args[1];
-            String description = (String) args[2];
-
-            StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId);
-            StudentRecordType recordType = studentRecord.getStudentRecordType();
-
-            try {
-                metricsService.recordCompletion(recordType, description);
-            } catch (Exception e) {
-                // 메트릭 수집 실패는 로그만 남기고 비즈니스 로직은 계속 진행
-                log.warn("Error collecting completion metrics for recordType: {}", recordType, e);
-            }
-        }
+        try {
+            Object[] args = joinPoint.getArgs();
+            if (args.length == 3
+                    && args[0] instanceof Long
+                    && args[1] instanceof Long
+                    && args[2] instanceof String) {
+                long memberId = (Long) args[0];
+                long recordId = (Long) args[1];
+                String description = (String) args[2];
+
+                StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId);
+                StudentRecordType recordType = studentRecord.getStudentRecordType();
+                metricsService.recordCompletion(recordType, description);
+            }
+        } catch (Exception e) {
+            log.warn("Error collecting completion metrics", e);
+        }
     }

46-80: Around 어드바이스도 동일하게 격리 필요

DB 조회 및 Redis 접근 전반을 try-catch로 감싸 예외가 비즈니스 로직에 영향 주지 않게 하세요.

     @Around("@annotation(com.edukit.common.annotation.AIGenerationMetrics)")
     public Object collectAIGenerationMetrics(final ProceedingJoinPoint joinPoint) throws Throwable {
-        Object[] args = joinPoint.getArgs();
-
-        if (args.length == 4) {
-            long memberId = (Long) args[0];
-            long recordId = (Long) args[1];
-
-            StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId);
-            StudentRecordType recordType = studentRecord.getStudentRecordType();
-
-            try {
-                boolean isFirstGeneration = recordGenerationTracker.isFirstGeneration(recordId);
-                // 전체 AI 생성 요청 카운트
-                metricsService.recordAIGenerationRequest(recordType);
-                // 첫 생성 vs 재생성 구분 메트릭
-                if (isFirstGeneration) {
-                    metricsService.recordFirstGeneration(recordType);
-                    log.debug("First generation request for recordId: {}", recordId);
-                } else {
-                    metricsService.recordRegeneration(recordType);
-                    log.debug("Regeneration request for recordId: {}", recordId);
-                }
-            } catch (Exception e) {
-                // 메트릭 수집 실패는 로그만 남기고 비즈니스 로직은 계속 진행
-                log.warn("Error collecting AI generation metrics for recordId: {}", recordId, e);
-            }
-        }
-
-        // 비즈니스 로직은 반드시 1회만 실행, 예외는 그대로 전파
-        return joinPoint.proceed();
+        try {
+            Object[] args = joinPoint.getArgs();
+            if (args.length == 4 && args[0] instanceof Long && args[1] instanceof Long) {
+                long memberId = (Long) args[0];
+                long recordId = (Long) args[1];
+
+                StudentRecord studentRecord = studentRecordService.getRecordDetail(memberId, recordId);
+                StudentRecordType recordType = studentRecord.getStudentRecordType();
+
+                boolean isFirstGeneration = recordGenerationTracker.isFirstGeneration(recordId);
+                metricsService.recordAIGenerationRequest(recordType);
+                if (isFirstGeneration) {
+                    metricsService.recordFirstGeneration(recordType);
+                    log.debug("First generation request for recordId: {}", recordId);
+                } else {
+                    metricsService.recordRegeneration(recordType);
+                    log.debug("Regeneration request for recordId: {}", recordId);
+                }
+            }
+        } catch (Exception e) {
+            log.warn("Error collecting AI generation metrics", e);
+        }
+        return joinPoint.proceed();
     }
🧹 Nitpick comments (6)
edukit-core/src/main/java/com/edukit/core/studentrecord/metric/StudentRecordMetricsCounter.java (1)

46-53: 완료 기준(minBytes) 하드코딩 제거 및 설정화 제안

타입별 최소 바이트 임계치가 하드코딩되어 있어 릴리스 없이 조정이 어렵습니다. 설정값으로 추출하세요.

-    private boolean isCompleted(final StudentRecordType type, final String description) {
+    private boolean isCompleted(final StudentRecordType type, final String description) {
         if (description == null || description.trim().isEmpty()) {
             return false;
         }
-
-        int minBytes = (type == StudentRecordType.SUBJECT) ? 1000 : 750;
+        int minBytes = (type == StudentRecordType.SUBJECT) ? minSubjectBytes : minDefaultBytes;
         return description.getBytes(StandardCharsets.UTF_8).length >= minBytes;
     }

추가로 클래스 필드에 다음을 선언해 주세요:

@org.springframework.beans.factory.annotation.Value("${studentrecord.metrics.min_bytes.subject:1000}")
private int minSubjectBytes;

@org.springframework.beans.factory.annotation.Value("${studentrecord.metrics.min_bytes.default:750}")
private int minDefaultBytes;
docker-compose.dev.yml (1)

18-19: JAR/설정 파일 마운트는 읽기 전용으로

에이전트 JAR와 설정 파일을 :ro로 마운트해 우발적 변경을 방지하세요.

-      - /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
-      - ./jmx-config.yml:/app/jmx-config.yml
+      - /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar:ro
+      - ./jmx-config.yml:/app/jmx-config.yml:ro

Also applies to: 39-40

docker-compose.prod.yml (1)

18-19: 읽기 전용 마운트로 불변성 강화

운영 구성/바이너리 파일은 :ro로 마운트하세요.

-      - /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar
-      - ./jmx-config.yml:/app/jmx-config.yml
+      - /opt/jmx_prometheus_javaagent.jar:/app/jmx_prometheus_javaagent.jar:ro
+      - ./jmx-config.yml:/app/jmx-config.yml:ro

Also applies to: 39-40

edukit-core/src/main/java/com/edukit/core/studentrecord/metric/StudentRecordMetricsAspect.java (1)

29-33: 인자 포지션/타입 가정 완화 제안

포인트컷 대상 메서드 시그니처가 바뀌면 조용히 스킵되거나 ClassCastException이 발생할 수 있습니다. 어노테이션에 파라미터 메타데이터를 추가하거나, @AfterReturning(pointcut=..., returning="ret")로 반환 객체에서 타입을 얻는 방식을 검토하세요.

해당 어노테이션이 붙은 모든 메서드 시그니처가 (memberId: Long, recordId: Long, ...) 순서를 보장하는지 확인 부탁드립니다.

Also applies to: 50-56

jmx-config.yml (2)

11-17: Tomcat Connector 패턴도 하이픈 미매치

Connector 이름에도 하이픈이 포함됩니다. 패턴을 수정하세요.

-  - pattern: 'Catalina<type=GlobalRequestProcessor, name="(\w+)"><>(requestCount|bytesReceived|bytesSent): (\d+)'
+  - pattern: 'Catalina<type=GlobalRequestProcessor, name="([^"]+)"><>(requestCount|bytesReceived|bytesSent): (\d+)'

64-64: 파일 말미 개행 누락

YAML 린트 에러를 제거하려면 마지막 줄에 개행을 추가하세요.

-    type: GAUGE
\ No newline at end of file
+    type: GAUGE
+
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7b1ee2 and d306984.

📒 Files selected for processing (9)
  • .gitignore (1 hunks)
  • docker-compose.dev.yml (2 hunks)
  • docker-compose.prod.yml (2 hunks)
  • edukit-api/src/main/java/com/edukit/studentrecord/controller/StudentRecordAIController.java (0 hunks)
  • edukit-core/src/main/java/com/edukit/core/common/converter/StudentRecordTypeConverter.java (0 hunks)
  • edukit-core/src/main/java/com/edukit/core/studentrecord/metric/RecordGenerationTracker.java (1 hunks)
  • edukit-core/src/main/java/com/edukit/core/studentrecord/metric/StudentRecordMetricsAspect.java (1 hunks)
  • edukit-core/src/main/java/com/edukit/core/studentrecord/metric/StudentRecordMetricsCounter.java (1 hunks)
  • jmx-config.yml (1 hunks)
💤 Files with no reviewable changes (2)
  • edukit-core/src/main/java/com/edukit/core/common/converter/StudentRecordTypeConverter.java
  • edukit-api/src/main/java/com/edukit/studentrecord/controller/StudentRecordAIController.java
🧰 Additional context used
🪛 YAMLlint (1.37.1)
jmx-config.yml

[error] 64-64: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (4)
edukit-core/src/main/java/com/edukit/core/studentrecord/metric/RecordGenerationTracker.java (1)

21-27: Lua 스크립트로 INCR+EXPIRE 원자화 잘 처리됨

초기 증가에만 TTL을 설정해 윈도우 내 재생성을 구분하는 의도에 맞습니다. 이 부분은 그대로 가도 좋습니다.

운영에 적용 전, record.generation.ttl을 0으로 설정했을 때 키가 만료되지 않는 것이 의도인지 확인 부탁드립니다.

Also applies to: 31-36

.gitignore (1)

167-168: 에이전트 JAR ignore 추가 LGTM

에이전트 바이너리 추적 방지에 적절합니다.

호스트 경로가 /opt/jmx_prometheus_javaagent.jar 외 다른 위치를 쓰는 서버가 있는지 점검해 주세요(있다면 해당 경로 패턴도 함께 ignore 필요).

jmx-config.yml (1)

29-36: GC 메트릭 네이밍과 타입 적절 — LGTM

컬렉션 횟수/시간을 COUNTER로 분리하고 GC 이름을 라벨로 둔 점 좋습니다.

Also applies to: 37-43

docker-compose.dev.yml (1)

9-9: JAVA_OPTS 전달 여부 수동 확인 필요

파일: docker-compose.dev.yml — 줄 9, 30에 설정한 JAVA_OPTS가 베이스 이미지의 ENTRYPOINT/시작 스크립트에서 실제로 JVM으로 전달되는지 수동으로 확인하세요. 자동 검사에서 관련 파일을 찾지 못했습니다.

  • 확인 방법: Dockerfile 및 ENTRYPOINT(또는 시작 스크립트)를 열어 'JAVA_OPTS' 참조 여부 또는 JVM 실행 명령(java ...)에 해당 옵션이 포함되는지 확인. 베이스 이미지가 'JAVA_TOOL_OPTIONS'만 인식하면 JAVA_TOOL_OPTIONS로 옮기거나 entrypoint에서 JAVA_OPTS를 JVM 커맨드에 포함시키도록 수정하세요.

Comment thread docker-compose.dev.yml
Comment thread docker-compose.prod.yml
Comment thread jmx-config.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
.claude/CLAUDE.md (6)

13-20: 코드 블록에 언어 지정 누락(MD040) — 간단히 text 지정 권장

마크다운 린트 경고를 해소하려면 언어를 지정하세요.

-```
+```text
 edukit-api (REST API Layer)
 ├── edukit-core (Business Logic)
 │   └── edukit-common (Shared Utilities)
 └── edukit-external (External Integrations)
     ├── edukit-core
     └── edukit-common
-```
+```

115-123: 패키지 구조 코드 블록도 언어 지정 필요(MD040)

디렉터리 레이아웃 예시는 text가 적절합니다.

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

60-62: 멀티모듈에서 bootRun 대상 명시

루트에서 bootRun은 오작동할 수 있습니다. API 모듈을 명시하세요.

-# Run application locally
-./gradlew bootRun
+# Run application locally (API module)
+./gradlew :edukit-api:bootRun

93-103: JMX Java Agent 사용 예시 추가 제안

운영 가이드 강화를 위해 -javaagent 예시와 로컬 바인딩 권고를 바로 여기에 넣어두면 좋습니다.

   - Environment variables managed via `.env` files
   - JMX monitoring configured in `jmx-config.yml`
+
+#### JMX Prometheus Java Agent 예시 (로컬 바인딩 권장)
+```bash
+# bind to loopback to avoid public exposure
+-javaagent:/opt/jmx_prometheus_javaagent.jar=127.0.0.1:8081:/opt/jmx/jmx-config.yml
+```
+외부 노출이 필요한 경우 반드시 방화벽/보안그룹/네트워크 정책으로 접근을 제한하세요.

134-139: 민감 데이터/프롬프트 로그 처리 지침 보강

AI 연동 특성상 프롬프트/응답과 학생 식별자가 로그·메트릭으로 유출되지 않도록 명시해두는 것이 안전합니다.

 ### 🔐 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
+ - 프롬프트/응답 본문은 로그로 남기지 않기(샘플링·마스킹 적용, PII 제거)
+ - 메트릭/로그에는 학생 ID·이메일 등 고유 식별자 사용 금지(대신 해시/익명화 키 사용)

153-157: 메트릭 레이블 카디널리티/수집 주기 가이드 추가

운영 관점에서 레이블 폭증과 scrape 주기 과도 설정은 비용·성능 이슈로 직결됩니다. 간단한 원칙을 문서에 추가하세요.

 ### 🔄 Metrics Collection
 - Custom metrics using `@Aspect` and Micrometer
 - Student record generation tracking
 - Performance monitoring for AI operations
+ - 레이블 카디널리티 관리: 사용자/요청 ID 등 고카디널리티 필드 금지, bounded set만 허용
+ - 수집 주기: 기본 15s 권장(고비용 메트릭은 30–60s), 테스트 환경은 더 길게
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d306984 and 54be744.

📒 Files selected for processing (2)
  • .claude/CLAUDE.md (1 hunks)
  • jmx-config.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • jmx-config.yml
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
.claude/CLAUDE.md

13-13: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


115-115: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread .claude/CLAUDE.md
@wldks1008 wldks1008 merged commit 1425f31 into develop Sep 21, 2025
2 checks passed
@wldks1008 wldks1008 deleted the feat/EDMT-453 branch September 21, 2025 17:05
@wldks1008 wldks1008 self-assigned this Oct 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant