Skip to content

feat: 사용자 피드백 제안 API#129

Merged
chaechaepower merged 2 commits intomainfrom
feat/#128-suggestions
Apr 28, 2026
Merged

feat: 사용자 피드백 제안 API#129
chaechaepower merged 2 commits intomainfrom
feat/#128-suggestions

Conversation

@chaechaepower
Copy link
Copy Markdown
Member

@chaechaepower chaechaepower commented Apr 28, 2026

🧷 이슈

🔨 작업 내용

  • 사용자 피드백 제안 기능 구현

👀 리뷰 요구사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 사용자 피드백 및 제안 제출 기능 추가
    • 제출된 피드백은 이메일을 통해 자동으로 전송됨
  • 설정

    • 이메일 발송을 위한 SMTP 구성 추가

@chaechaepower chaechaepower self-assigned this Apr 28, 2026
@chaechaepower chaechaepower added the ✨ Feature 기능 개발 label Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Walkthrough

사용자 피드백 제안 기능을 추가합니다. Spring Mail 의존성을 도입하고, 피드백 요청 수신, 사용자 검증, 이메일 전송을 담당하는 컨트롤러, 서비스, 메일 모듈을 구현하며, SMTP 설정을 추가합니다.

Changes

Cohort / File(s) 요약
Spring Mail 의존성 및 설정
build.gradle, src/main/resources/application.yml
Spring Boot mail starter 의존성 추가 및 Gmail SMTP 설정(smtp.gmail.com:587, 환경 변수 기반 인증, STARTTLS 활성화)
피드백 API 레이어
src/main/java/kr/co/webee/presentation/feedback/api/FeedbackApi.java, src/main/java/kr/co/webee/presentation/feedback/controller/FeedbackController.java
피드백 제안 API 인터페이스 정의 및 /api/v1/suggestions POST 엔드포인트 구현
피드백 요청 DTO
src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.java
@NotBlank 검증이 적용된 content 필드를 포함하는 피드백 요청 DTO
피드백 서비스 로직
src/main/java/kr/co/webee/application/feedback/service/FeedbackService.java
사용자 ID 기반 사용자 검증 및 메일 서비스 호출
메일 전송 구현
src/main/java/kr/co/webee/infrastructure/mail/MailService.java
HTML 형식의 이메일 구성 및 전송, 실패 시 BusinessException 반환
에러 타입 정리
src/main/java/kr/co/webee/common/error/ErrorType.java
TAMPERED_ACCESS_TOKEN (AUTH_005), UNSUPPORTED_JWT_TOKEN (AUTH_006) 상수 제거

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Controller as FeedbackController
    participant Service as FeedbackService
    participant UserRepo as UserRepository
    participant MailService as MailService
    participant JavaMail as JavaMailSender
    
    User->>Controller: POST /api/v1/suggestions (FeedbackSubmitRequest)
    Controller->>Service: submitFeedback(request, userId)
    Service->>UserRepo: findById(userId)
    alt User exists
        UserRepo-->>Service: User
        Service->>MailService: sendEmail(content, userName)
        MailService->>JavaMail: createMimeMessage()
        JavaMail-->>MailService: MimeMessage
        MailService->>JavaMail: send(message)
        JavaMail-->>MailService: Success
        MailService-->>Service: (void)
        Service-->>Controller: (void)
        Controller-->>User: "OK" (200)
    else User not found
        UserRepo-->>Service: Empty
        Service-->>Controller: EntityNotFoundException
        Controller-->>User: Error (400)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 메일의 바다에 피드백을 띄우네,
사용자의 목소리가 모여 하나의 편지가 되고,
Spring의 손길로 이메일 날개를 달고,
제안은 우편함에 날아가 반짝이네. ✉️

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning ErrorType.java에서 인증 관련 enum 상수 2개(TAMPERED_ACCESS_TOKEN, UNSUPPORTED_JWT_TOKEN)가 제거되었는데, 이는 피드백 제안 API 구현 범위 외의 변경사항입니다. ErrorType enum 상수 제거는 별도의 이슈나 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 제목 'feat: 사용자 피드백 제안 API'는 변경사항의 주요 내용을 명확하게 요약하고 있습니다. 피드백 제안 API 구현이 핵심 변경사항이며, 제목이 이를 직접적으로 반영합니다.
Linked Issues check ✅ Passed 이슈 #128의 요구사항인 '사용자 피드백 제안 API' 구현이 완전히 충족되었습니다. API 엔드포인트, 서비스 계층, 메일 발송 기능, DTO, 설정 등 모든 필요한 코드가 구현되어 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#128-suggestions

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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
Copy Markdown

@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: 3

🤖 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/kr/co/webee/infrastructure/mail/MailService.java`:
- Line 30: The email body in MailService (helper.setText(...)) directly injects
user-provided name and content into HTML, creating XSS/injection risk; escape or
sanitize both name and content before building the HTML (e.g., apply an
HTML-encoding function such as StringEscapeUtils.escapeHtml4 or Spring's
HtmlUtils.htmlEscape to the name and content) and then use the escaped values in
helper.setText, or alternatively send the user content in the plain-text part
only and keep the HTML template free of raw user input.

In
`@src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.java`:
- Around line 9-10: The FeedbackSubmitRequest DTO's content field currently only
uses `@NotBlank` and needs a maximum length constraint to avoid oversized
payloads; add `@Size`(max = 2000) to the content field in the
FeedbackSubmitRequest class (and import javax.validation.constraints.Size if
missing) so validation enforces the 2000-character limit alongside the existing
`@NotBlank`.

In `@src/main/resources/application.yml`:
- Around line 46-52: The SMTP timeout config only sets mail.smtp.timeout (read
timeout) and can leave threads blocked during connect/write; update the SMTP
settings by adding mail.smtp.connectiontimeout and mail.smtp.writetimeout
alongside the existing properties.mail.smtp.timeout (and keep
mail.smtp.starttls.enable and mail.smtp.auth) with appropriate millisecond
values so connection, read, and write phases are all protected.
🪄 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: cd35ce91-b6e0-4747-a6d1-32e44594a447

📥 Commits

Reviewing files that changed from the base of the PR and between 42cdfeb and 69d626c.

📒 Files selected for processing (8)
  • build.gradle
  • src/main/java/kr/co/webee/application/feedback/service/FeedbackService.java
  • src/main/java/kr/co/webee/common/error/ErrorType.java
  • src/main/java/kr/co/webee/infrastructure/mail/MailService.java
  • src/main/java/kr/co/webee/presentation/feedback/api/FeedbackApi.java
  • src/main/java/kr/co/webee/presentation/feedback/controller/FeedbackController.java
  • src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.java
  • src/main/resources/application.yml
💤 Files with no reviewable changes (1)
  • src/main/java/kr/co/webee/common/error/ErrorType.java


helper.setTo(email);
helper.setSubject(title);
helper.setText("<h3>" + name + "님이 남긴 피드백입니다.</h3><p>" + "</p>" + content, true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

HTML 본문에 사용자 입력을 그대로 넣는 방식은 주입 리스크가 있습니다.

Line [30]에서 name, content를 이스케이프 없이 HTML로 렌더링하고 있어, 관리자 메일에 악성 링크/태그가 삽입될 수 있습니다.

🔧 제안 수정안
 import org.springframework.mail.javamail.JavaMailSender;
 import org.springframework.mail.javamail.MimeMessageHelper;
 import org.springframework.stereotype.Service;
+import org.springframework.web.util.HtmlUtils;
@@
-            helper.setText("<h3>" + name + "님이 남긴 피드백입니다.</h3><p>" + "</p>" + content, true);
+            String safeName = HtmlUtils.htmlEscape(name);
+            String safeContent = HtmlUtils.htmlEscape(content).replace("\n", "<br/>");
+            helper.setText("<h3>" + safeName + "님이 남긴 피드백입니다.</h3><p>" + safeContent + "</p>", true);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
helper.setText("<h3>" + name + "님이 남긴 피드백입니다.</h3><p>" + "</p>" + content, true);
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;
// ... other code ...
String safeName = HtmlUtils.htmlEscape(name);
String safeContent = HtmlUtils.htmlEscape(content).replace("\n", "<br/>");
helper.setText("<h3>" + safeName + "님이 남긴 피드백입니다.</h3><p>" + safeContent + "</p>", true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/kr/co/webee/infrastructure/mail/MailService.java` at line 30,
The email body in MailService (helper.setText(...)) directly injects
user-provided name and content into HTML, creating XSS/injection risk; escape or
sanitize both name and content before building the HTML (e.g., apply an
HTML-encoding function such as StringEscapeUtils.escapeHtml4 or Spring's
HtmlUtils.htmlEscape to the name and content) and then use the escaped values in
helper.setText, or alternatively send the user content in the plain-text part
only and keep the HTML template free of raw user input.

Comment on lines +9 to +10
@NotBlank
String content
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Spring Boot 3 with Jakarta Validation, does @notblankenforce a maximum input length, or should@SiZe(max=...) be added explicitly?

💡 Result:

@NotBlank does not enforce a maximum input length. @Size(max=...) must be added explicitly for maximum length validation. In Spring Boot 3, which uses Jakarta Bean Validation (typically via Hibernate Validator), @NotBlank ensures the string is not null and contains at least one non-whitespace character after trimming (trimmed length > 0). It performs no upper bound check on length. The official Jakarta Bean Validation 3.0 API documentation for @NotBlank states: "The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence." No mention of max length. Hibernate Validator's NotBlankValidator implementation confirms: "Checks that the character sequence is not null nor empty after removing any leading or trailing whitespace." It returns true if trimmed length > 0, without max length logic. Examples from sources consistently use @NotBlank alongside @Size(max=...) for max length, e.g., @NotBlank(message="Message is required") @Size(max=500, message="Message cannot exceed 500 characters"). @Size is the separate constraint for length bounds (min/max), applicable to strings, collections, etc.

Citations:


🏁 Script executed:

find . -type f -name "FeedbackSubmitRequest.java" | head -20

Repository: BeeKeeprs/was

Length of output: 138


🏁 Script executed:

cat -n ./src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.java

Repository: BeeKeeprs/was

Length of output: 493


피드백 본문에 최대 길이 제한이 필요합니다.

@NotBlank는 공백만 검증하며 최대 길이를 제한하지 않습니다. 길이 제한이 없으면 과도한 요청이 그대로 메일 발송 단계까지 진행되어 서버 리소스를 낭비할 수 있습니다. 명시적으로 @Size(max = 2000)을 추가하세요.

🔧 수정안
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
@@
         `@Schema`(description = "내용", example = "수정벌 추천 기능에 대해 건의 드립니다. ...")
         `@NotBlank`
+        `@Size`(max = 2000)
         String content
 ) {
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@NotBlank
String content
`@NotBlank`
`@Size`(max = 2000)
String content
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.java`
around lines 9 - 10, The FeedbackSubmitRequest DTO's content field currently
only uses `@NotBlank` and needs a maximum length constraint to avoid oversized
payloads; add `@Size`(max = 2000) to the content field in the
FeedbackSubmitRequest class (and import javax.validation.constraints.Size if
missing) so validation enforces the 2000-character limit alongside the existing
`@NotBlank`.

Comment on lines +46 to +52
properties:
mail:
smtp:
auth: true
timeout: 5000
starttls:
enable: true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# SMTP 타임아웃 설정(read/connect/write) 확인
rg -n -C3 'spring:|mail:|smtp:|connectiontimeout|writetimeout|timeout' src/main/resources/application.yml

Repository: BeeKeeprs/was

Length of output: 565


🌐 Web query:

JavaMail SMTP timeout properties mail.smtp.connectiontimeout mail.smtp.writetimeout documentation

💡 Result:

The JavaMail (now Jakarta Mail) SMTP timeout properties are documented in the official com.sun.mail.smtp package summary. They control different aspects of socket timeouts when connecting to and communicating with an SMTP server. All values are integers representing milliseconds; defaults are infinite timeout (-1). - mail.smtp.connectiontimeout: Socket connection timeout value in milliseconds. Implemented using java.net.Socket. - mail.smtp.timeout: Socket read timeout value in milliseconds (for reading server responses). Implemented using java.net.Socket. - mail.smtp.writetimeout: Socket write timeout value in milliseconds (for sending data to the server). Implemented using a java.util.concurrent.ScheduledExecutorService per connection (one extra thread per connection). These are set as JavaMail Session properties, e.g.: Properties props = new Properties; props.setProperty("mail.smtp.connectiontimeout", "5000"); props.setProperty("mail.smtp.timeout", "5000"); props.setProperty("mail.smtp.writetimeout", "5000"); Session session = Session.getInstance(props); Note: These timeouts apply per operation (connect/read/write), not to the entire send operation. Multiple operations may occur sequentially during sending, so total time can exceed individual timeouts. For SMTPS (SSL), use mail.smtps.* equivalents.

Citations:


SMTP 타임아웃 설정이 불완전해서 요청 스레드가 장시간 묶일 수 있습니다.

현재 설정에 mail.smtp.timeout(read timeout)만 있어 SMTP 연결 및 데이터 전송 단계에서 스레드 블로킹이 발생할 수 있습니다. mail.smtp.connectiontimeoutmail.smtp.writetimeout을 함께 설정해야 모든 단계에서 타임아웃 보호가 가능합니다.

🔧 제안 수정안
   mail:
     host: smtp.gmail.com
     port: 587
     username: ${MAIL_USERNAME:}
     password: ${MAIL_PASSWORD:}
     properties:
       mail:
         smtp:
           auth: true
+          connectiontimeout: 5000
           timeout: 5000
+          writetimeout: 5000
           starttls:
             enable: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
properties:
mail:
smtp:
auth: true
timeout: 5000
starttls:
enable: true
properties:
mail:
smtp:
auth: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
starttls:
enable: true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/application.yml` around lines 46 - 52, The SMTP timeout
config only sets mail.smtp.timeout (read timeout) and can leave threads blocked
during connect/write; update the SMTP settings by adding
mail.smtp.connectiontimeout and mail.smtp.writetimeout alongside the existing
properties.mail.smtp.timeout (and keep mail.smtp.starttls.enable and
mail.smtp.auth) with appropriate millisecond values so connection, read, and
write phases are all protected.

@chaechaepower chaechaepower merged commit bc64bdb into main Apr 28, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

사용자 피드백 제안 API

1 participant