Write Once, Validate Anywhere.
(Java & JavaScript) ํ ๋ฒ์ ์์ฑ์ผ๋ก ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋๋ฅผ ๊ฒ์ฆํ๋ ๊ฐ์ฅ ์ค๋งํธํ ๋ฐฉ๋ฒ.
S2Util is a high-performance Java utility library featuring a Unified Dynamic Validator that seamlessly synchronizes validation logic between Server (Java) and Client (JavaScript). Designed for production-ready environments, it leverages advanced technologies like Method Handles and intelligent caching to ensure maximum efficiency and type safety.
S2Util์ ์๋ฒ(Java)์ ํด๋ผ์ด์ธํธ(JavaScript) ๊ฐ์ ๊ฒ์ฆ ๋ก์ง์ ์๋ฒฝํ๊ฒ ๋๊ธฐํํ๋ ํตํฉ ๋์ ๊ฒ์ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ๋ก๋์ ๊ธ(Production-ready) ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ๋ชฉํ๋ก ์ค๊ณ๋์์ผ๋ฉฐ, Method Handle ๋ฐ ์ง๋ฅํ ์บ์ฑ๊ณผ ๊ฐ์ ์ฒจ๋จ ๊ธฐ์ ์ ํ์ฉํ์ฌ ์ต์ ์ ์คํ ์๋์ ์ ์ ํ์ ์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
Add the following dependencies to your build.gradle (using Maven Central).
dependencies {
implementation 'io.github.devers2:s2-util:1.1.6'
}
// [Optional] S2Validator Static Analysis Plugin
// โจ Prevent runtime errors caused by typos or field name mismatches.
// When using Generics (e.g., S2Validator.<UserCommand>builder()), this plugin performs
// static analysis during the build to verify that all referenced field names
// actually exist in the specified DTO class.
// It triggers a build error if a non-existent field is detected.
// (์ ๋ค๋ฆญ์ ์ฌ์ฉํ ๊ฒฝ์ฐ(์: S2Validator.<UserCommand>builder()), ๋น๋ ์์ ์ ์ ์ ๋ถ์์ ์ํํฉ๋๋ค.
// ๋ช
์๋ DTO์ ์ค์ ํ๋๊ฐ ์๋์ง ํ์ธํ์ฌ, ์คํ ๋ฑ์ผ๋ก ์กด์ฌํ์ง ์๋ ํ๋๋ฅผ ์ฐธ์กฐํ๋ฉด
// ๋น๋ ์๋ฌ๋ฅผ ๋ฐ์์์ผ ๋ฐํ์ ์ค๋ฅ๋ฅผ ์๋ฒฝํ ์๋ฐฉํฉ๋๋ค.)
plugins {
id 'io.github.devers2.validator' version '1.1.1'
}Unified validation for server and client.
Note: This example assumes Spring Framework integration. If Spring is not available, you can use
S2ValidatorandS2ValidatorFactorydirectly, butBindingResultintegration will not be available.
(์ฐธ๊ณ : ์ด ์์ ๋ Spring Framework ํตํฉ์ ๊ฐ์ ํฉ๋๋ค. Spring์ด ์๋ ํ๊ฒฝ์์๋S2Validator๋ฐS2ValidatorFactory๋ฅผ ์ง์ ์ฌ์ฉํ์ฌ ๊ฒ์ฆํ ์ ์์ผ๋,BindingResult์ฐ๋์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.)
private S2Validator<UserCommand> profileValidator() {
return S2Validator.<UserCommand>builder()
// If no rule is specified, S2RuleType.REQUIRED is applied by default
// (Rule์ด ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก REQUIRED ์ ์ฉ)
// "Name" is the label used in error messages (์๋ฌ ๋ฉ์์ง์ ์ฌ์ฉ๋ ๋ผ๋ฒจ)
.field("name", "Name")
.field("password", "Password")
// When specifying explicit rules, REQUIRED must be added manually if needed
// (์ง์ Rule ์ง์ ์ ํ์ ์ฒดํฌ๊ฐ ํ์ํ๋ฉด REQUIRED ๋ณ๋ ์ง์ )
.field("passwordCheck", "Confirm Password")
.rule(S2RuleType.REQUIRED)
// Verifies value equals "password" field (password ํ๋์ ๋์ผํ ๊ฐ์ธ์ง ๊ฒ์ฆ)
.rule(S2RuleType.EQUALS_FIELD, "password")
// Set English error message (์๋ฌธ ์๋ฌ ๋ฉ์์ง ์ค์ )
.en("Password check does not match.")
.message(Locale.ENGLISH, "Password check does not match.")
// Set Korean error message (ํ๊ธ ์๋ฌ ๋ฉ์์ง ์ค์ )
.ko("๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.")
// Set Hindi error message (ํ๋์ด ์๋ฌ ๋ฉ์์ง ์ค์ )
.message(Locale.forLanguageTag("hi"), "เคชเคพเคธเคตเคฐเฅเคก เคฎเฅเคฒ เคจเคนเฅเค เคเคพเคคเฅ.")
.field("userType", "User Type")
.field("paymentMethod", "Payment Method")
.field("cardNumber", "Card Number")
// โจ Conditional validation: cardNumber if (USER + CREDIT_CARD) OR (SELLER)
// (์ผ๋ฐํ์์ ์นด๋๊ฒฐ์ ๊ฑด ๋๋ ํ๋งค์์ผ ๊ฒฝ์ฐ ์นด๋๋ฒํธ ๊ฒ์ฆ)
.when("userType", "USER").and("paymentMethod", "CREDIT_CARD")
.when("userType", "SELLER")
.build();
}
@GetMapping("/sign-up")
public String signUpPage(@ModelAttribute("command") UserCommand command, Model model) {
// Convert validator to JSON and pass to client for validation
// (ํด๋ผ์ด์ธํธ ์ ํจ์ฑ ๊ฒ์ฆ์ ์ํด JSON์ผ๋ก ๋ณํํ์ฌ ์ ๋ฌ)
model.addAttribute("rules", S2BindValidator.context("sign-up", this::profileValidator).getRulesJson());
return "sign-up";
}
@PostMapping("/sign-up")
public String signUp(@ModelAttribute("command") UserCommand command, BindingResult result, Model model) {
// Perform server-side validation using the same validator configuration
// (์ค์ ๋ ๊ฒ์ฆ๊ธฐ๋ก ์๋ฒ ์ธก์์๋ ๋์ผํ๊ฒ ๊ฒ์ฆ ์ํ)
S2BindValidator.context("sign-up", this::profileValidator).validate(command, result);
if (result.hasErrors()) {
return signUpPage(command, model);
}
userService.createUser(command);
return "redirect:/sign-in";
}<!-- Inject the validation rules JSON string passed from the controller -->
<!-- ์ปจํธ๋กค๋ฌ์์ ์ ๋ฌ๋ฐ์ ๊ฒ์ฆ ๊ท์น(JSON ๋ฌธ์์ด)์ ํผ์ data ์์ฑ์ ์ฃผ์
-->
<form id="myForm" th:data-s2-rules="${rules}">...</form>
<script type="module">
// s2.validator.js is served automatically from the JAR's META-INF/resources.
// (s2.validator.js๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ(META-INF/resources)์ ํฌํจ๋์ด ์์ด ๋ณ๋ ์ค์ ์์ด ๋ฐ๋ก ๋ก๋๋ฉ๋๋ค.)
import '/s2-util/js/s2.validator.js';
// Just importing the script automatically performs validation using the browser's native UI during submit, matching the server-side rules.
// (์ํฌํธ๋ง ํ๋ฉด ํผ ์ ์ก ์ ๋ธ๋ผ์ฐ์ ๋ค์ดํฐ๋ธ UI๋ฅผ ํตํด ์๋ฒ์ ๋์ผํ ๊ฒ์ฆ์ด ์๋์ผ๋ก ์ํ๋ฉ๋๋ค.)
</script>The foundational library providing high-performance core utility classes. Features include:
- High-Performance Reflection: Method Handle-based reflection with JIT optimization
- Intelligent Caching: Built-in optimized lightweight cache (concurrent-safe, zero-dependency) with optional Caffeine support for extreme high-concurrency environments
- Java Version-Adaptive Thread Factory: Virtual Thread support (Java 21+) with platform thread fallback
- Optimized Data Access:
getValue()andsetValue()with dot notation and bracket indexing support - Comprehensive Utilities: String manipulation, date/time handling, type conversion, and more
๊ณ ์ฑ๋ฅ ํต์ฌ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ์ ๊ณตํ๋ ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ฃผ์ ๊ธฐ๋ฅ:
- ๊ณ ์ฑ๋ฅ ๋ฆฌํ๋ ์ : Method Handle ๊ธฐ๋ฐ ๋ฆฌํ๋ ์ (JIT ์ต์ ํ)
- ์ง๋ฅํ ์บ์ฑ: ์ธ๋ถ ์์กด์ฑ ์๋ ์์ฒด ๊ณ ์ฑ๋ฅ ๋์์ฑ ๊ฒฝ๋ ์บ์ ์ ๊ณต (๋๊ท๋ชจ ํธ๋ํฝ ํ๊ฒฝ์ ์ํ ์ ํ์ Caffeine ์ง์)
- ์๋ฐ ๋ฒ์ ์ ์ํ ์ค๋ ๋ ํฉํ ๋ฆฌ: ๊ฐ์ ์ค๋ ๋ ์ง์ (Java 21+) ๋ฐ ํ๋ซํผ ์ค๋ ๋ ํด๋ฐฑ
- ์ต์ ํ๋ ๋ฐ์ดํฐ ์ ๊ทผ: ์ ํ๊ธฐ๋ฒ ๋ฐ ๋๊ดํธ ์ธ๋ฑ์ฑ ์ง์
- ์ข ํฉ ์ ํธ๋ฆฌํฐ: ๋ฌธ์์ด ์กฐ์, ๋ ์ง/์๊ฐ ์ฒ๋ฆฌ, ํ์ ๋ณํ ๋ฑ
A unified cross-platform validation Library supporting both server and client with single configuration. Features include:
- Fluent API: Natural, chainable validation rules with sequential method application
- 30+ Built-in Rule Types: REQUIRED, LENGTH, REGEX, EMAIL, MPHONE_NO, DATE, and more
- Korea-specific Rules: MPHONE_NO, TEL_NO, ZIP, BIZRNO, NWINO, JUMIN, PASSWORD_ANSWR
- Advanced Nested Object Support: Dot notation (
user.address.street) and bracket indexing (items[0]) - Comprehensive i18n: Message localization with
ko(),en(), custom locales, andS2ResourceBundle - Custom & Conditional Validation:
CustomRuleinterface andwhen()/and()conditional logic - Spring Integration (Optional):
S2BindValidatorwithBindingResultfor standard Spring error handling
๋จ์ผ ์ค์ ์ผ๋ก ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋๋ฅผ ์ง์ํ๋ ํตํฉ ๊ฒ์ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ฃผ์ ๊ธฐ๋ฅ:
- ์ ์ฐํ API: ์์ฐ์ค๋ฌ์ด ์ฒด์ด๋ ๊ฒ์ฆ ๊ท์น
- 30๊ฐ์ง ์ด์ ๋ด์ฅ ๊ท์น: REQUIRED, LENGTH, REGEX, EMAIL, MPHONE_NO, DATE ๋ฑ
- ํ๊ตญ ์ ์ฉ ๊ท์น: MPHONE_NO, TEL_NO, ZIP, BIZRNO, NWINO, JUMIN, PASSWORD_ANSWR
- ๊ณ ๊ธ ์ค์ฒฉ ๊ฐ์ฒด ์ง์: ์ ํ๊ธฐ๋ฒ ๋ฐ ๋๊ดํธ ์ธ๋ฑ์ฑ
- ํฌ๊ด์ i18n: ๋ก์ปฌ๋ผ์ด์ ์ด์ ๋ฐ S2ResourceBundle ํตํฉ
- ์ปค์คํ ๋ฐ ์กฐ๊ฑด๋ถ ๊ฒ์ฆ: CustomRule ์ธํฐํ์ด์ค์ when()/and() ์กฐ๊ฑด ๋ก์ง
- Spring ํตํฉ (์ ํ์ฌํญ): BindingResult๋ฅผ ํตํ ํ์ค Spring ์๋ฌ ์ฒ๋ฆฌ
A Gradle build plugin for static source code analysis to validate S2Validator field names at compile-time. Features include:
- Static Analysis: JavaParser AST parsing for accurate code analysis
- Compile-Time Validation: Detects typos and non-existent fields before runtime
- Multi-Project Support: Scans all subprojects and modules
- Zero Configuration: Automatically integrates with standard Gradle build tasks
- Smart Validation: Skips validation for generic wildcards and incomplete type information
- Detailed Error Reporting: Color-coded messages with file paths and line numbers
S2Validator ํ๋๋ช ์ ์ปดํ์ผ ํ์์ ์ ์ ๋ถ์์ผ๋ก ๊ฒ์ฆํ๋ Gradle ๋น๋ ํ๋ฌ๊ทธ์ธ์ ๋๋ค. ์ฃผ์ ๊ธฐ๋ฅ:
- ์ ์ ๋ถ์: JavaParser AST ํ์ฑ์ผ๋ก ์ ํํ ์ฝ๋ ๋ถ์
- ์ปดํ์ผ ํ์ ๊ฒ์ฆ: ๋ฐํ์ ์ด์ ์ ์คํ์ ์กด์ฌํ์ง ์๋ ํ๋ ๊ฐ์ง
- ๋ค์ค ํ๋ก์ ํธ ์ง์: ๋ชจ๋ ์๋ธํ๋ก์ ํธ ๋ฐ ๋ชจ๋ ์ค์บ
- ์ค์ ๋ถํ์: ํ์ค Gradle ๋น๋ ํ์คํฌ ์๋ ํตํฉ
- ์ค๋งํธ ๊ฒ์ฆ: ์ ๋ค๋ฆญ ์์ผ๋์นด๋ ๋ฐ ๋ถ์์ ํ ํ์ ์ ๋ณด๋ ๊ฒ์ฆ ์คํต
- ์์ธ ์๋ฌ ๋ณด๊ณ : ์์ ์ฝ๋ฉ๋ ๋ฉ์์ง์ ํ์ผ ๊ฒฝ๋ก/์ค ๋ฒํธ
This project is built with JDK 21, but it can be used reliably in all environments running Java 17 or higher.
๋ณธ ํ๋ก์ ํธ๋ JDK 21 ํ๊ฒฝ์์ ๋น๋๋์์ผ๋, Java 17 ์ด์์ ๋ชจ๋ ํ๊ฒฝ์์ ์์ ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
This library is provided under the Apache License 2.0. You are free to use, modify, and distribute this software, provided that you comply with the obligations of the license (such as copyright notice and source code disclosure requirements). For detailed terms and conditions, please refer to the LICENSE file.
- Copyright 2020 - 2026 devers2 (์ด์น์, Daejeon, Korea)
- Contact: eseungsu.dev@gmail.com
Third-party Notice: This project uses external libraries. For detailed third-party license notices, please refer to the licenses/NOTICE file.
๋ณธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Apache License 2.0 ํ์ ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ฉ์๋ ๋ผ์ด์ ์ค์ ์๋ฌด ์ฌํญ(์ ์๊ถ ๊ณ ์ง, ์์ค ์ฝ๋ ๊ณต๊ฐ ๋ฒ์ ๋ฑ)์ ์ค์ํ๋ ์กฐ๊ฑด ํ์ ์์ ๋กญ๊ฒ ์ฌ์ฉ, ์์ ๋ฐ ์ฌ๋ฐฐํฌ๊ฐ ๊ฐ๋ฅํฉ๋๋ค. ์์ธํ ์กฐ๊ฑด์ LICENSE ํ์ผ์ ๋ฐ๋์ ํ์ธํด ์ฃผ์ธ์.
- ์ ์๊ถ 2020 - 2026 devers2 (์ด์น์, ๋ํ๋ฏผ๊ตญ ๋์ )
- ๋ฌธ์: eseungsu.dev@gmail.com
์ 3์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ณ ์ง: ๋ณธ ํ๋ก์ ํธ๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์์ธํ ์ 3์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ณ ์ง์ฌํญ์ licenses/NOTICE ํ์ผ์ ์ฐธ์กฐํด ์ฃผ์ธ์.
To use certain functionalities (e.g., S2BindValidator), the end-user project must explicitly add the following dependencies to be available at runtime. Failure to include these dependencies will result in a java.lang.NoClassDefFoundError at runtime.
[For Gradle Users]
dependencies {
// Essential runtime dependencies for optional functionalities
implementation 'com.github.ben-manes.caffeine:caffeine:3.2.3'
implementation 'org.springframework:spring-context:6.2.15'
implementation 'jakarta.persistence:jakarta.persistence-api:3.2.0'
}