Skip to content

fix: AI 스레드 내 멘션 오류 수정#392

Merged
JanooGwan merged 4 commits intodevelopfrom
feature/cam-205
Mar 13, 2026
Merged

fix: AI 스레드 내 멘션 오류 수정#392
JanooGwan merged 4 commits intodevelopfrom
feature/cam-205

Conversation

@JanooGwan
Copy link
Contributor

🔍 개요


🚀 주요 변경 내용

  • message 이벤트에서 isAppMention 체크 제거: 스레드 내 다른 앱(@linear 등) 멘션 시 AI가 호출되던 문제 수정
  • app_mention 이벤트에서만 멘션 처리하도록 변경 (AI) 접두사 방식은 유지)
  • Claude 응답의 텍스트 마크다운을 Slack mrkdwn 텍스트 볼드로 변환하는 convertMarkdownToSlack 메서드 추가

💬 참고 사항


✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

- message 이벤트에서 isAppMention 체크 제거: 스레드 내 다른 앱(@linear 등) 멘션 시 AI가 호출되던 문제 수정
- app_mention 이벤트에서만 멘션 처리하도록 변경 (AI) 접두사 방식은 유지)
- Claude 응답의 **텍스트** 마크다운을 Slack mrkdwn *텍스트* 볼드로 변환하는 convertMarkdownToSlack 메서드 추가
@JanooGwan JanooGwan requested a review from Copilot March 13, 2026 07:53
@JanooGwan JanooGwan self-assigned this Mar 13, 2026
@JanooGwan JanooGwan added 버그 정상적으로 동작하지 않는 문제 상황 관련 이슈입니다. 리팩토링 리팩터링을 위한 이슈입니다. labels Mar 13, 2026
@linear
Copy link

linear bot commented Mar 13, 2026

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Warning

Rate limit exceeded

@JanooGwan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 637f08b2-003e-4a5c-a4ee-0d473c7768c2

📥 Commits

Reviewing files that changed from the base of the PR and between bcfbe96 and f16367c.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
📝 Walkthrough

Walkthrough

Slack AI 응답의 마크다운 굵은 텍스트를 Slack 형식으로 변환하는 로직을 추가하고, 이벤트 컨트롤러에서 스레드 후속 처리 분기와 일부 로직을 제거·정리하며 이벤트 캐시 회수 로직과 로그 메시지들을 한국어 메시지로 변경했습니다.

Changes

Cohort / File(s) Summary
Slack AI 응답 포맷팅
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
MARKDOWN_BOLD_PATTERN 정규식 추가 및 convertMarkdownToSlack(String) 헬퍼 구현. formatSlackResponse(String)이 AI 응답에 접두사(AI_RESPONSE_PREFIX)를 추가하고 변환 함수를 적용하도록 변경. 로케일 메시지(EMPTY_QUERY_MESSAGE, ERROR_MESSAGE)를 한국어로 수정.
Slack 이벤트 컨트롤러 및 캐시/로깅 변경
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
앱 멘션 처리에서 기존의 스레드 후속 분기(스레드 기반 AI 후속 처리) 제거 및 processAIQuery 호출 흐름 단순화(스레드가 있을 때만 aiReplies 조회 후 전달). processedEventIds 캐시가 최대 크기 초과 시 오래된 항목 제거 로직 추가. 여러 로그/에러 메시지를 한국어로 변경.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 마크다운이 깜빡이면 내가 와서
굵게 바꿔주고 슬랙에 띄워줄게요.
스레드는 정리되고 로그는 한국말로,
토끼는 깡충깡충 기뻐하며 축배를 들어요. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 스레드 내 AI 멘션 오류 수정을 명확하게 설명하며, 실제 변경사항의 주요 목적과 일치합니다.
Description check ✅ Passed PR 설명은 문제 상황, 주요 변경 내용, 그리고 메서드 추가 등을 구체적으로 설명하며 변경사항과 관련이 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/cam-205
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Slack 스레드 내에서 다른 앱(@linear 등) 멘션 시 AI가 잘못 호출되던 버그를 수정하고, Claude 응답의 Markdown 볼드(**text**)를 Slack mrkdwn 볼드(*text*)로 변환하는 기능을 추가한 PR입니다.

Changes:

  • message 이벤트 핸들러에서 isAppMention 체크 분기를 제거하여 다른 앱 멘션에 반응하지 않도록 수정
  • app_mention 이벤트에서만 멘션을 처리하도록 분리 (기존 AI) 접두사 방식은 유지)
  • convertMarkdownToSlack 메서드를 추가하여 Claude 응답의 **bold**를 Slack *bold*로 변환

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
SlackEventController.java message 이벤트에서 isAppMention 분기 제거, java.util.List import 제거
SlackAIService.java MARKDOWN_BOLD_PATTERN 추가, convertMarkdownToSlack 메서드 추가, formatSlackResponse에서 변환 적용

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java`:
- Line 25: The MARKDOWN_BOLD_PATTERN in SlackAIService (private static final
Pattern MARKDOWN_BOLD_PATTERN) doesn't match bold markdown spanning multiple
lines; update its compilation to allow DOTALL (e.g.,
Pattern.compile("\\*\\*(.+?)\\*\\*", Pattern.DOTALL)) or change the inner token
to a multiline-safe class like "[\\s\\S]+?" so that inputs like
"**line1\nline2**" are correctly matched and converted.
- Around line 173-175: convertMarkdownToSlack can NPE when text is null because
MARKDOWN_BOLD_PATTERN.matcher(text) will throw; update convertMarkdownToSlack to
guard against null input (e.g., if text == null return "" or return null-safe
value) before calling MARKDOWN_BOLD_PATTERN.matcher, then proceed with
MARKDOWN_BOLD_PATTERN.matcher(text).replaceAll("*$1*"); ensure the null-guard is
added in the convertMarkdownToSlack method to make failure mode explicit and
easier to debug.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 51f0ad4b-7bbe-4843-b190-2f10fb1a398b

📥 Commits

Reviewing files that changed from the base of the PR and between 31cb6bb and 75e2e8f.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
💤 Files with no reviewable changes (1)
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cleanup artifacts
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
🔇 Additional comments (1)
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java (1)

177-179: LGTM!

마크다운 볼드 변환 로직이 기존 formatSlackResponse 메서드에 자연스럽게 통합되었습니다. PR 목적에 부합하는 변경입니다.

- app_mention 핸들러에서 threadTs 존재 시 fetchAIThreadReplies 호출해 대화 히스토리 전달
- convertMarkdownToSlack에 null 방어 코드 추가
- MARKDOWN_BOLD_PATTERN에 Pattern.DOTALL 플래그 추가해 멀티라인 볼드 지원
- formatSlackResponse에서 AI_RESPONSE_PREFIX 상수 활용으로 변경
@github-actions
Copy link

⚠️ Checkstyle 위반 사항 발견

Checkstyle 검사에서 코딩 컨벤션 위반이 발견되었습니다.

📋 상세 리포트

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java (1)

76-78: ⚠️ Potential issue | 🟠 Major

[LEVEL: medium] ConcurrentHashMap.newKeySet()은 삽입 순서를 보장하지 않아 "가장 오래된" 항목 제거가 불가능합니다.

iterator().next()는 임의의 요소를 반환하므로 최근 이벤트가 제거되고 오래된 이벤트가 남아 중복 처리 방지가 실패할 수 있습니다. Collections.newSetFromMap(new LinkedHashMap<>())을 synchronized로 래핑하거나, 별도의 ConcurrentLinkedQueue로 삽입 순서를 추적하세요.

♻️ LinkedHashSet 기반 수정 예시
-    private final Set<String> processedEventIds = ConcurrentHashMap.newKeySet();
+    private final Set<String> processedEventIds = 
+        Collections.synchronizedSet(new LinkedHashSet<>());

또는 lock-free 방식이 필요하면:

private final ConcurrentLinkedQueue<String> eventIdQueue = new ConcurrentLinkedQueue<>();
private final Set<String> processedEventIds = ConcurrentHashMap.newKeySet();

// 삽입 시
if (processedEventIds.add(eventId)) {
    eventIdQueue.add(eventId);
}
// 제거 시
while (processedEventIds.size() > EVENT_CACHE_MAX_SIZE) {
    String oldest = eventIdQueue.poll();
    if (oldest != null) {
        processedEventIds.remove(oldest);
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java`
around lines 76 - 78, The current removal logic in SlackEventController uses
processedEventIds.iterator().next() on a ConcurrentHashMap.newKeySet() which
does not preserve insertion order, so replace this with an ordered strategy:
either wrap a LinkedHashSet via Collections.synchronizedSet(...) (or
Collections.newSetFromMap(new LinkedHashMap<>())) for deterministic
oldest-element removal, or add a ConcurrentLinkedQueue<String> eventIdQueue
alongside the existing processedEventIds and push IDs into the queue when added;
then, when processedEventIds.size() > EVENT_CACHE_MAX_SIZE, poll the queue to
get the oldest ID and remove that ID from processedEventIds (repeat until under
limit). Ensure additions use processedEventIds.add(eventId) and only enqueue
when add returns true, and update the removal code that currently calls
iterator().next() to use the chosen ordered-removal approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java`:
- Around line 29-32: The two long string constants EMPTY_QUERY_MESSAGE and
ERROR_MESSAGE in SlackAIService exceed the 120-char line limit; fix by splitting
each literal into multiple shorter string literals concatenated with + across
lines (e.g., break the Unicode-escaped message into parts and join into
EMPTY_QUERY_MESSAGE and ERROR_MESSAGE) or move the messages into a properties
resource and load them (e.g., messages.properties + ResourceBundle) and replace
the constants with loaded values; ensure the final assembled strings remain
identical and update any references to EMPTY_QUERY_MESSAGE / ERROR_MESSAGE
accordingly.

---

Outside diff comments:
In
`@src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java`:
- Around line 76-78: The current removal logic in SlackEventController uses
processedEventIds.iterator().next() on a ConcurrentHashMap.newKeySet() which
does not preserve insertion order, so replace this with an ordered strategy:
either wrap a LinkedHashSet via Collections.synchronizedSet(...) (or
Collections.newSetFromMap(new LinkedHashMap<>())) for deterministic
oldest-element removal, or add a ConcurrentLinkedQueue<String> eventIdQueue
alongside the existing processedEventIds and push IDs into the queue when added;
then, when processedEventIds.size() > EVENT_CACHE_MAX_SIZE, poll the queue to
get the oldest ID and remove that ID from processedEventIds (repeat until under
limit). Ensure additions use processedEventIds.add(eventId) and only enqueue
when add returns true, and update the removal code that currently calls
iterator().next() to use the chosen ordered-removal approach.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ecc6bf21-29d4-4691-9ad3-bcfb3314cc87

📥 Commits

Reviewing files that changed from the base of the PR and between 75e2e8f and bcfbe96.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java
  • src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
🪛 GitHub Actions: Checkstyle
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java

[error] 30-30: Checkstyle: LineLength violation. Line is longer than 120 characters (found 207).


[error] 32-32: Checkstyle: LineLength violation. Line is longer than 120 characters (found 160).

🔇 Additional comments (4)
src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java (3)

25-27: LGTM!

이전 리뷰에서 지적된 Pattern.DOTALL 플래그가 적용되어 멀티라인 볼드 텍스트를 올바르게 처리합니다.


174-179: LGTM!

이전 리뷰에서 지적된 null 방어 코드가 적절히 추가되었습니다.


181-183: LGTM!

AI_RESPONSE_PREFIX 상수를 활용하여 응답 포맷팅이 일관되게 처리됩니다.

src/main/java/gg/agit/konect/infrastructure/slack/ai/SlackEventController.java (1)

121-133: LGTM!

스레드 컨텍스트 존재 여부에 따라 fetchAIThreadReplies를 호출하고, 결과를 processAIQuery에 전달하는 로직이 PR 목적에 맞게 구현되었습니다.

@JanooGwan JanooGwan merged commit 75b3543 into develop Mar 13, 2026
3 checks passed
@JanooGwan JanooGwan deleted the feature/cam-205 branch March 13, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

리팩토링 리팩터링을 위한 이슈입니다. 버그 정상적으로 동작하지 않는 문제 상황 관련 이슈입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants