Skip to content

[FIX] 문서 NUL 문자 정제 및 Discord 알림 길이 제한 - #199

Merged
gdbs1107 merged 3 commits into
developfrom
fix/#198/doc
Jun 1, 2026
Merged

[FIX] 문서 NUL 문자 정제 및 Discord 알림 길이 제한#199
gdbs1107 merged 3 commits into
developfrom
fix/#198/doc

Conversation

@gdbs1107

@gdbs1107 gdbs1107 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

📣 Related Issue



📝 Summary

  • 문서 NUL 문자 정제 및 Discord 알림 길이 제한

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 문서 입력값의 널 문자 자동 제거 기능 추가
  • 개선사항

    • Discord 알림 메시지 길이를 2,000자로 제한하여 전송 안정성 향상
  • 테스트

    • 널 문자 제거 동작 검증 테스트 추가
    • Discord 메시지 길이 제한 테스트 추가

@gdbs1107 gdbs1107 self-assigned this Jun 1, 2026
@gdbs1107 gdbs1107 added 🐛 fix 🐛 버그 수정 💪🏼 재연 labels Jun 1, 2026
@gdbs1107 gdbs1107 linked an issue Jun 1, 2026 that may be closed by this pull request
1 task
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@gdbs1107, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 6 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b069eef7-1560-44fc-ad1f-e4193b0f9dbe

📥 Commits

Reviewing files that changed from the base of the PR and between 2ae138c and 08bd842.

📒 Files selected for processing (5)
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/CreateDocumentBlockRequest.java
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java
  • ssd-api/src/test/java/or/hyu/ssd/api/document/request/CreateDocumentRequestValidationTest.java
  • ssd-external/src/main/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifier.java
  • ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java

요약

이 PR은 두 가지 독립적인 입력 데이터 정제 기능을 추가합니다: 문서 생성 요청에서 null 문자를 제거하는 기능과 Discord 알림 메시지를 2000자로 제한하는 기능입니다.

변경 사항

문서 요청 null 문자 제거

레이어 / 파일 요약
RequestStringSanitizer 정제 유틸리티
ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java
null 문자(\u0000) 제거를 담당하는 패키지 전용 유틸리티 클래스를 추가합니다. stripNullChar() 정적 메서드는 입력이 null이거나 null 문자를 포함하지 않으면 원본을 반환하고, 포함하면 null 문자를 제거합니다.
문서 요청 클래스 정제 적용
ssd-api/src/main/java/or/hyu/ssd/api/document/request/CreateDocumentRequest.java, CreateDocumentBlockRequest.java, UpdateDocumentRequest.java
CreateDocumentRequest, UpdateDocumentRequest, CreateDocumentBlockRequest의 toCommand() 메서드에서 title, text, content, role 등 문자열 필드에 RequestStringSanitizer.stripNullChar()를 적용하여 null 문자 제거된 값을 커맨드 객체에 전달합니다.
문서 정제 기능 테스트
ssd-api/src/test/java/or/hyu/ssd/api/document/request/CreateDocumentRequestValidationTest.java
toCommandStripsNullCharacter() 테스트를 추가하여 title, text, 문단 블록의 content와 role 필드에 포함된 null 문자가 정제되는 동작을 검증합니다.

Discord 메시지 길이 제한

레이어 / 파일 요약
DiscordWebhookNotifier 길이 제한 구현
ssd-external/src/main/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifier.java
Discord API의 2000자 제한에 맞춘 메시지 길이 제한 기능을 추가합니다. DISCORD_CONTENT_LIMIT(2000) 상수와 TRUNCATION_SUFFIX(\n...(truncated)) 생략 표시를 정의하고, truncateForDiscord() 메서드로 초과 시 접미사 길이를 고려하여 문자열을 자르며, buildPayload()에서 JSON escape 전에 적용합니다.
Discord 길이 제한 테스트
ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java
truncateMessageWhenContentTooLong()keepMessageWhenWithinLimit() 테스트를 추가하여 리플렉션으로 비공개 truncateForDiscord() 메서드를 호출해 2000자 초과 메시지의 자르기 동작과 범위 내 메시지의 보존 동작을 각각 검증합니다.

예상 검토 노력

🎯 3 (보통) | ⏱️ ~20분

제안 라벨

⚙️ chore

널 문자 제거하고 Discord도 정리하며,
요청 데이터는 깔끔하게, 메시지는 잘라내며,
안정성을 높이는 이 겨울,
사용자에게 깨끗한 경험을 선물하네 🐰✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning DiscordWebhookNotifier의 길이 제한 기능은 #198 이슈의 명시적 범위를 초과합니다. 이슈는 문서 생성 시 NUL 문자 정제에만 초점을 두고 있으며, Discord 알림 길이 제한은 별도의 기능 추가입니다. Discord 길이 제한 기능을 별도 PR로 분리하거나, 해당 기능을 다루는 연관 이슈를 링크하여 범위를 명확히 하세요.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 변경사항을 명확하게 요약하고 있습니다. NUL 문자 정제와 Discord 알림 길이 제한이라는 두 가지 핵심 변경을 간결하게 표현합니다.
Linked Issues check ✅ Passed PR은 #198 이슈의 주요 요구사항인 문서 생성 중 NUL 문자 파싱 오류 해결을 구현했습니다. RequestStringSanitizer를 통해 CreateDocumentRequest, CreateDocumentBlockRequest, UpdateDocumentRequest에서 NUL 문자를 정제하고 있습니다.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/#198/doc

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.

@gdbs1107

gdbs1107 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

📝 Code Coverage

Overall Project NaN% NaN% 🍏

There is no coverage information present for the Files changed

@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

🧹 Nitpick comments (3)
ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java (1)

8-13: 💤 Low value

문자 리터럴과 문자열 리터럴의 일관성 개선 권장

Line 9에서는 '\u0000' (char 리터럴)을 사용하고 Line 12에서는 "\u0000" (String 리터럴)을 사용하고 있습니다. 동작에는 문제가 없지만, 가독성을 위해 동일한 표현 방식을 사용하는 것이 좋습니다.

♻️ 일관성 개선 제안
 static String stripNullChar(String value) {
-    if (value == null || value.indexOf('\u0000') < 0) {
+    if (value == null || !value.contains("\u0000")) {
         return value;
     }
     return value.replace("\u0000", "");
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java`
around lines 8 - 13, The stripNullChar method uses a char literal '\u0000' in
value.indexOf(...) but a String literal "\u0000" in value.replace(...); make
these consistent (prefer using the String literal "\u0000" for both calls or the
char form for both) by updating the indexOf call or the replace call in
stripNullChar so both use the same literal form to improve readability and
consistency.
ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java (2)

12-40: ⚡ Quick win

null 입력 처리에 대한 테스트 케이스 추가를 권장합니다.

현재 구현부(DiscordWebhookNotifier line 137)에서 null 입력 시 null을 반환하는데, 이는 escapeJson에서 NPE를 유발할 수 있습니다. null 입력에 대한 테스트를 추가하면 이런 이슈를 조기에 발견할 수 있습니다.

♻️ null 테스트 추가 제안
`@Test`
`@DisplayName`("디스코드 본문이 null이면 빈 문자열을 반환한다")
void returnEmptyStringWhenContentIsNull() throws Exception {
    DiscordWebhookNotifier notifier = new DiscordWebhookNotifier(new DiscordProperties(), new MockEnvironment());
    Method truncateMethod = DiscordWebhookNotifier.class.getDeclaredMethod("truncateForDiscord", String.class);
    truncateMethod.setAccessible(true);

    String truncated = (String) truncateMethod.invoke(notifier, (String) null);

    assertThat(truncated).isEqualTo("");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java`
around lines 12 - 40, The truncateForDiscord method in DiscordWebhookNotifier
currently returns null for null input which can cause a NullPointerException
later in escapeJson; change truncateForDiscord to handle null by returning an
empty string instead, and add a unit test (as suggested) that invokes
DiscordWebhookNotifier.truncateForDiscord with a null argument and asserts it
returns "" to prevent NPEs in escapeJson and downstream logic.

14-26: ⚡ Quick win

테스트에 내용 검증을 추가하면 더 견고해집니다.

현재 테스트는 길이와 접미사 존재 여부만 확인합니다. 잘린 메시지의 앞부분이 원본 내용을 올바르게 보존하는지도 검증하면 truncation 로직의 정확성을 더 확실히 보장할 수 있습니다.

♻️ 내용 검증 추가 제안
 String longMessage = "a".repeat(2100);
 String truncated = (String) truncateMethod.invoke(notifier, longMessage);

 assertThat(truncated.length()).isLessThanOrEqualTo(2000);
 assertThat(truncated).endsWith("\n...(truncated)");
+assertThat(truncated).startsWith(longMessage.substring(0, 1983));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java`
around lines 14 - 26, Update the test truncateMessageWhenContentTooLong to also
verify that the truncated string preserves the original beginning content: after
invoking DiscordWebhookNotifier.truncateForDiscord, assert that the returned
value starts with the original message's first N characters (e.g., compare
substring(0, expectedPrefixLen) of the original longMessage with substring(0,
expectedPrefixLen) of truncated) in addition to the existing length and suffix
checks; locate the test method truncateMessageWhenContentTooLong in
DiscordWebhookNotifierTest and the truncateForDiscord method in
DiscordWebhookNotifier to determine the appropriate prefix length to assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@ssd-api/src/main/java/or/hyu/ssd/api/document/request/CreateDocumentBlockRequest.java`:
- Around line 40-47: The validation currently runs before NUL characters are
stripped so ROLE_PATTERN rejects inputs like "\u0000##"; update the
CreateDocumentBlockRequest validation to sanitize role before validating by
calling RequestStringSanitizer.stripNullChar(role) inside
isValidBlockStructure() (or the validation method) and use that sanitizedRole
for ROLE_PATTERN.matcher(...).matches() and other checks; ensure resolvedType()
and other branching logic remain the same and keep toCommand() unchanged (it can
still strip defensively), so validation operates on the cleaned value rather
than the raw input.

In
`@ssd-api/src/test/java/or/hyu/ssd/api/document/request/CreateDocumentRequestValidationTest.java`:
- Around line 67-88: The test currently calls CreateDocumentRequest.toCommand()
directly which bypasses bean validation; add a new test that submits the same
request to the validation step (use VALIDATOR.validate(request)) to assert
whether violations are empty, referencing CreateDocumentRequest and
CreateDocumentBlockRequest; then modify the validation logic in
CreateDocumentBlockRequest.isValidBlockStructure() (or adjust ROLE_PATTERN) to
either strip NUL characters before applying ROLE_PATTERN or update ROLE_PATTERN
to allow leading/trailing NULs so the request that contains "\u0000##" will pass
validation and the downstream toCommand() sanitization remains effective.

In
`@ssd-external/src/main/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifier.java`:
- Around line 136-146: The truncateForDiscord method currently returns null when
content is null which later causes a NullPointerException in buildPayload ->
escapeJson (due to .replace on null); update truncateForDiscord so it never
returns null (return an empty string or other safe default) and still apply
truncation logic (use DISCORD_CONTENT_LIMIT and TRUNCATION_SUFFIX as before),
ensuring buildPayload and escapeJson always receive non-null input.

---

Nitpick comments:
In
`@ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java`:
- Around line 8-13: The stripNullChar method uses a char literal '\u0000' in
value.indexOf(...) but a String literal "\u0000" in value.replace(...); make
these consistent (prefer using the String literal "\u0000" for both calls or the
char form for both) by updating the indexOf call or the replace call in
stripNullChar so both use the same literal form to improve readability and
consistency.

In
`@ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java`:
- Around line 12-40: The truncateForDiscord method in DiscordWebhookNotifier
currently returns null for null input which can cause a NullPointerException
later in escapeJson; change truncateForDiscord to handle null by returning an
empty string instead, and add a unit test (as suggested) that invokes
DiscordWebhookNotifier.truncateForDiscord with a null argument and asserts it
returns "" to prevent NPEs in escapeJson and downstream logic.
- Around line 14-26: Update the test truncateMessageWhenContentTooLong to also
verify that the truncated string preserves the original beginning content: after
invoking DiscordWebhookNotifier.truncateForDiscord, assert that the returned
value starts with the original message's first N characters (e.g., compare
substring(0, expectedPrefixLen) of the original longMessage with substring(0,
expectedPrefixLen) of truncated) in addition to the existing length and suffix
checks; locate the test method truncateMessageWhenContentTooLong in
DiscordWebhookNotifierTest and the truncateForDiscord method in
DiscordWebhookNotifier to determine the appropriate prefix length to assert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed350f31-1c23-4b64-ae9f-6199d43fc6ba

📥 Commits

Reviewing files that changed from the base of the PR and between af040b9 and 2ae138c.

📒 Files selected for processing (7)
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/CreateDocumentBlockRequest.java
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/CreateDocumentRequest.java
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/RequestStringSanitizer.java
  • ssd-api/src/main/java/or/hyu/ssd/api/document/request/UpdateDocumentRequest.java
  • ssd-api/src/test/java/or/hyu/ssd/api/document/request/CreateDocumentRequestValidationTest.java
  • ssd-external/src/main/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifier.java
  • ssd-external/src/test/java/or/hyu/ssd/external/alert/discord/DiscordWebhookNotifierTest.java

@gdbs1107
gdbs1107 merged commit 1ece0e0 into develop Jun 1, 2026
2 checks passed
@gdbs1107
gdbs1107 deleted the fix/#198/doc branch June 1, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 fix 🐛 버그 수정 💪🏼 재연

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] 문서 생성 중, \u0000 파싱 오류 해결

1 participant