Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import jakarta.validation.constraints.*;
import moadong.club.enums.ApplicationFormMode;
import moadong.club.enums.SemesterTerm;
import org.springframework.util.StringUtils;

import java.util.List;

public record ClubApplicationFormEditRequest(
@NotBlank
@Size(max = 50)
String title,

Expand All @@ -29,4 +31,25 @@ public record ClubApplicationFormEditRequest(

SemesterTerm semesterTerm
) {

@AssertTrue(message = "지원서 양식에 필요한 필드가 누락되었습니다.")
private boolean isInternalFormValid() {
if (formMode != ApplicationFormMode.INTERNAL) {
return true;
}

boolean hasDescription = StringUtils.hasText(description);
boolean hasQuestions = questions != null && !questions.isEmpty();

return hasDescription && hasQuestions;
}

@AssertTrue(message = "외부 링크가 누락되었습니다.")
private boolean isExternalFormValid() {
if (formMode != ApplicationFormMode.EXTERNAL) {
return true;
}

return StringUtils.hasText(externalApplicationUrl);
}
Comment on lines +35 to +54
Copy link
Contributor

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:

# First, let's examine the actual file to confirm the code snippet
cat -n backend/src/main/java/moadong/club/payload/request/ClubApplicationFormEditRequest.java | head -60

Repository: Moadong/moadong

Length of output: 1760


🏁 Script executed:

# Search for other `@AssertTrue` usage in the codebase to understand patterns
rg -n --type java '@AssertTrue' -A 2 -B 1

Repository: Moadong/moadong

Length of output: 2125


🏁 Script executed:

# Check if there are any other validation constraint methods (private or public) in the codebase
rg -n --type java '(private|public)\s+boolean\s+is[A-Z]' -A 1 | head -50

Repository: Moadong/moadong

Length of output: 2973


🌐 Web query:

Spring DataBinder @AssertTrue private method NotReadablePropertyException validation

💡 Result:

In Spring MVC / DataBinder-triggered Bean Validation (@Valid on a form/model attribute), Hibernate Validator validates bean properties (fields or JavaBeans getters). If you put @AssertTrue on a private helper method that is not a JavaBeans getter, Spring/HV won’t treat it as a constraint to execute; instead you can end up with Spring trying to resolve it as a property and failing with NotReadablePropertyException (no readable getter for that “property”). (stackoverflow.com)

Fixes

  1. Make it a real JavaBeans boolean getter and annotate that

    `@AssertTrue`(message = "...")
    public boolean isValid() { ... }   // no args; name starts with is/get

    This is the standard way for cross-field checks with @AssertTrue in bean validation. (stackoverflow.com)

  2. Or annotate a field instead of a method

    `@AssertTrue`(message="...")
    private boolean valid;
    public boolean isValid() { return valid; }

    (Field constraints don’t depend on getter naming; the validator can access fields directly.) (docs.hibernate.org)

  3. If you truly want “method validation” (arbitrary methods), don’t use DataBinder for that
    Use executable/method validation (e.g., Spring’s @Validated on service methods). DataBinder validation is about validating the bound bean, not invoking arbitrary private methods. (docs.spring.io)

Citations:


🌐 Web query:

Bean Validation @AssertTrue property constraint public getter requirement specification

💡 Result:

In Bean Validation, a property constraint such as @AssertTrue may be placed on a JavaBeans getter (i.e., the read accessor). The specification’s requirements are:

  • The constrained property accessor must follow JavaBeans getter conventions: getX() (no parameters) or, for boolean properties, isX() (no parameters, returns boolean). The property name is derived via java.beans.Introspector.decapitalize(...). [1]
  • Visibility is not constrained by the specification: “The fields or methods visibility are not constrained.” So a constrained getter is not required to be public by the Bean Validation spec (though some frameworks/contexts may impose additional rules outside the spec). [1]
  • Static fields and static methods are excluded from validation. [2]

Sources:
[1] Bean Validation spec, “Field and property validation” (JavaBeans conventions + visibility not constrained) https://beanvalidation.org/latest-draft/spec/
[2] Bean Validation spec, “Requirements on classes to be validated” (static excluded) https://beanvalidation.org/latest-draft/spec/


private 접근자의 @AssertTrue 메서드는 Spring의 데이터 바인더에서 유효성 검증 실패 시 문제를 유발할 수 있습니다.

Bean Validation 사양은 제약 조건을 private 메서드에 적용하는 것을 금지하지 않지만, Spring MVC의 DataBinder는 유효성 검증 실패 시 JavaBeans 규약에 따라 프로퍼티를 조회하려 시도합니다. 이 경우 internalFormValid, externalFormValid 프로퍼티가 접근 불가능하면 NotReadablePropertyException을 발생시켜 400 오류 대신 500 오류를 반환할 수 있습니다.

접근 제어자를 public으로 변경하여 JavaBeans 컨벤션을 준수하면 이 문제가 해결되며, 코드의 의도도 명확해집니다.

제안 수정
-    `@AssertTrue`(message = "지원서 양식에 필요한 필드가 누락되었습니다.")
-    private boolean isInternalFormValid() {
+    `@AssertTrue`(message = "지원서 양식에 필요한 필드가 누락되었습니다.")
+    public boolean isInternalFormValid() {

-    `@AssertTrue`(message = "외부 링크가 누락되었습니다.")
-    private boolean isExternalFormValid() {
+    `@AssertTrue`(message = "외부 링크가 누락되었습니다.")
+    public boolean isExternalFormValid() {

참고: 동일한 패턴이 ClubApplicationFormCreateRequest.java에도 존재하므로 함께 수정이 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/main/java/moadong/club/payload/request/ClubApplicationFormEditRequest.java`
around lines 34 - 53, Change the two private validation methods
isInternalFormValid() and isExternalFormValid() to public so they follow
JavaBeans conventions and avoid DataBinder NotReadablePropertyException during
validation; update the access modifier in ClubApplicationFormEditRequest for
both methods (and make the same change in ClubApplicationFormCreateRequest for
the equivalent methods) so Spring's validator can access the boolean properties
without causing 500 errors.

}
Loading