feat: 사용자 피드백 제안 API#129
Conversation
Walkthrough사용자 피드백 제안 기능을 추가합니다. Spring Mail 의존성을 도입하고, 피드백 요청 수신, 사용자 검증, 이메일 전송을 담당하는 컨트롤러, 서비스, 메일 모듈을 구현하며, SMTP 설정을 추가합니다. Changes
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the 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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
build.gradlesrc/main/java/kr/co/webee/application/feedback/service/FeedbackService.javasrc/main/java/kr/co/webee/common/error/ErrorType.javasrc/main/java/kr/co/webee/infrastructure/mail/MailService.javasrc/main/java/kr/co/webee/presentation/feedback/api/FeedbackApi.javasrc/main/java/kr/co/webee/presentation/feedback/controller/FeedbackController.javasrc/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.javasrc/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); |
There was a problem hiding this comment.
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.
| 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.
| @NotBlank | ||
| String content |
There was a problem hiding this comment.
🧩 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:
- 1: https://jakarta.ee/specifications/bean-validation/3.0/apidocs/jakarta/validation/constraints/notblank
- 2: https://docs.jboss.org/hibernate/stable/validator/api/org/hibernate/validator/internal/constraintvalidators/bv/NotBlankValidator.html
- 3: https://www.baeldung.com/java-bean-validation-not-null-empty-blank
- 4: https://stackoverflow.com/questions/79392344/why-does-sizemin-validation-trigger-before-notblank-validation-in-spring-boo
- 5: https://jakarta.ee/specifications/bean-validation/3.0/apidocs/jakarta/validation/constraints/package-summary.html
🏁 Script executed:
find . -type f -name "FeedbackSubmitRequest.java" | head -20Repository: BeeKeeprs/was
Length of output: 138
🏁 Script executed:
cat -n ./src/main/java/kr/co/webee/presentation/feedback/dto/FeedbackSubmitRequest.javaRepository: 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.
| @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`.
| properties: | ||
| mail: | ||
| smtp: | ||
| auth: true | ||
| timeout: 5000 | ||
| starttls: | ||
| enable: true |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# SMTP 타임아웃 설정(read/connect/write) 확인
rg -n -C3 'spring:|mail:|smtp:|connectiontimeout|writetimeout|timeout' src/main/resources/application.ymlRepository: 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:
- 1: https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html
- 2: https://javadoc.io/static/com.sun.mail/jakarta.mail/2.0.0/com/sun/mail/smtp/package-summary.html
- 3: https://artofcode.wordpress.com/2023/12/31/sending-e-mail-with-java-21-smtp-ssl-in-2024/
- 4: https://jakarta.ee/specifications/mail/1.6/apidocs/com/sun/mail/smtp/package-summary
- 5: https://stackoverflow.com/questions/18970409/why-javamail-connection-timeout-is-too-long
- 6: https://stackoverflow.com/questions/62698967/javamail-connection-timeout-is-not-working-as-per-properties
SMTP 타임아웃 설정이 불완전해서 요청 스레드가 장시간 묶일 수 있습니다.
현재 설정에 mail.smtp.timeout(read timeout)만 있어 SMTP 연결 및 데이터 전송 단계에서 스레드 블로킹이 발생할 수 있습니다. mail.smtp.connectiontimeout과 mail.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.
| 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.
🧷 이슈
🔨 작업 내용
👀 리뷰 요구사항
Summary by CodeRabbit
릴리스 노트
새로운 기능
설정