0Pricing
Java Academy · 강의

간결한 생성자와 유효성 검사

데이터 무결성을 보장하도록 간결한 생성자 내부에 유효성 검사 로직을 추가합니다.

간결한 생성자와 유효성 검사은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

간결한 생성자

레코드의 간결한 생성자는 컴포넌트가 할당되기 전에 실행됩니다. 컴포넌트 할당을 반복하지 않고 데이터를 검증하거나 정규화할 수 있습니다.

일반 생성자와 간결한 생성자 비교

일반적인 canonical 생성자는 컴포넌트를 명시적으로 할당합니다. 간결한 생성자는 매개변수 목록과 할당을 생략하며, 본문이 실행된 후 자동으로 처리됩니다.

// Standard canonical constructor
record Range(int min, int max) {
    Range(int min, int max) {
        if (min > max) throw new IllegalArgumentException("min > max");
        this.min = min;  // explicit assignment
        this.max = max;
    }
}

// Compact constructor (same behavior, less code)
record Range2(int min, int max) {
    Range2 {  // no parameter list, no assignments
        if (min > max) throw new IllegalArgumentException("min > max");
        // components are assigned automatically after this block
    }
}

간결한 생성자에서 검증하기

간결한 생성자는 레코드 데이터를 검증하는 표준적인 방법입니다. 불변 조건을 적용하려면 예외를 즉시 발생시키십시오.

record Email(String address) {
    Email {
        if (address == null || address.isBlank())
            throw new IllegalArgumentException("Email cannot be blank");
        if (!address.contains("@"))
            throw new IllegalArgumentException("Invalid email: " + address);
        address = address.toLowerCase().strip(); // normalize (Java 16+)
    }
}

Email e = new Email("  Alice@Example.com  ");
System.out.println(e.address()); // alice@example.com

try { new Email("not-an-email"); }
catch (IllegalArgumentException ex) { System.out.println(ex.getMessage()); }

간결한 생성자에서 정규화하기

간결한 생성자에서 컴포넌트 변수가 할당되기 전에 해당 변수를 수정할 수 있습니다. 이렇게 하면 생성 시 데이터가 정규화됩니다.

record PersonName(String firstName, String lastName) {
    PersonName {
        firstName = capitalize(firstName);
        lastName  = capitalize(lastName);
    }

    private static String capitalize(String s) {
        if (s == null || s.isEmpty()) return s;
        return Character.toUpperCase(s.charAt(0)) +
               s.substring(1).toLowerCase();
    }

    public String fullName() { return firstName + " " + lastName; }
}

PersonName name = new PersonName("jOHN", "DOE");
System.out.println(name.fullName()); // John Doe

방어적 복사

배열이나 컬렉션처럼 변경 가능한 컴포넌트는 간결한 생성자에서 방어적으로 복사하여 변경 불가능성을 유지하십시오.

import java.util.*;

record Snapshot(List<String> items) {
    Snapshot {
        items = List.copyOf(items); // defensive copy — unmodifiable
    }
}

List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
Snapshot snap = new Snapshot(mutable);
mutable.add("d"); // doesn't affect snapshot
System.out.println(snap.items()); // [a, b, c]

try {
    snap.items().add("e"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Snapshot is truly immutable!");
}

범위 제한이 있는 Range 레코드

min <= max를 보장하고 유용한 유틸리티 메서드를 제공하는 실용적인 Range 레코드입니다.

record Range(int min, int max) {
    Range {
        if (min > max) throw new IllegalArgumentException(
            "min (" + min + ") must be <= max (" + max + ")");
    }

    public boolean contains(int value) { return value >= min && value <= max; }
    public int size()  { return max - min; }
    public int clamp(int value) { return Math.max(min, Math.min(max, value)); }
}

Range valid = new Range(1, 10);
System.out.println(valid.contains(5));  // true
System.out.println(valid.clamp(15));    // 10
System.out.println(valid.size());       // 9

간결한 생성자 로직 연결하기

복잡한 검증이 필요하다면 도우미 메서드로 추출한 후 간결한 생성자에서 호출하십시오.

record CreditCard(String number, String cvv, int expiryMonth, int expiryYear) {
    CreditCard {
        validateNumber(number);
        validateCvv(cvv);
        validateExpiry(expiryMonth, expiryYear);
        number = number.replaceAll("[^0-9]", ""); // strip spaces/dashes
    }

    private static void validateNumber(String n) {
        String digits = n.replaceAll("[^0-9]", "");
        if (digits.length() < 13 || digits.length() > 19)
            throw new IllegalArgumentException("Invalid card number length");
    }
    private static void validateCvv(String cvv) {
        if (!cvv.matches("[0-9]{3,4}"))
            throw new IllegalArgumentException("Invalid CVV");
    }
    private static void validateExpiry(int m, int y) {
        if (m < 1 || m > 12) throw new IllegalArgumentException("Invalid month");
        if (y < 2024)         throw new IllegalArgumentException("Card expired");
    }
}

여러 간결한 생성자 패턴

간결한 생성자에서 사용하는 일반적인 검증 패턴입니다.

record Percentage(double value) {
    Percentage {
        if (value < 0 || value > 100)
            throw new IllegalArgumentException(
                "Percentage must be 0-100, got: " + value);
        value = Math.round(value * 100.0) / 100.0; // round to 2 dp
    }

    public double asFraction() { return value / 100.0; }
}

Percentage tax = new Percentage(8.756);
System.out.println(tax.value());       // 8.76
System.out.println(tax.asFraction()); // 0.0876

정식 생성자가 아닌 생성자

레코드에는 추가 생성자를 정의할 수 있지만, this(...)를 사용하여 canonical 생성자에 위임해야 합니다.

record Point(double x, double y) {
    // Non-canonical constructor: origin point
    Point() { this(0.0, 0.0); }

    // Non-canonical: polar coordinates
    static Point fromPolar(double r, double theta) {
        return new Point(r * Math.cos(theta), r * Math.sin(theta));
    }

    public double distance(Point other) {
        double dx = this.x - other.x;
        double dy = this.y - other.y;
        return Math.sqrt(dx*dx + dy*dy);
    }
}

Point origin = new Point();
Point p = Point.fromPolar(5, Math.PI/4);
System.out.printf("Distance: %.2f%n", origin.distance(p)); // 5.00

변경 불가능한 컬렉션 레코드

생성 시 사용자 환경설정을 검증하고 정규화하는 변경 불가능한 스냅샷을 보유한 레코드입니다.

import java.util.*;

record UserPreferences(String theme, Set<String> enabledFeatures, int fontSize) {
    private static final Set<String> VALID_THEMES = Set.of("light", "dark", "system");
    private static final Set<String> VALID_FEATURES = Set.of("ai", "beta", "analytics");

    UserPreferences {
        if (!VALID_THEMES.contains(theme))
            throw new IllegalArgumentException("Unknown theme: " + theme);
        if (!VALID_FEATURES.containsAll(enabledFeatures))
            throw new IllegalArgumentException("Unknown feature in: " + enabledFeatures);
        if (fontSize < 10 || fontSize > 24)
            throw new IllegalArgumentException("fontSize must be 10-24");
        enabledFeatures = Set.copyOf(enabledFeatures); // defensive copy
    }
}

간결한 생성자의 제한 사항

간결한 생성자에서는 다음 작업을 수행할 수 없습니다:

  • 컴포넌트를 명시적으로 할당할 수 없습니다(블록이 끝난 후 자동으로 할당됩니다)
  • this() 또는 super()를 호출할 수 없습니다
  • 예외를 발생시키면 모든 컴포넌트 할당이 중단됩니다
record Safe(int value) {
    Safe {
        // CAN: validate and modify component variables
        if (value < 0) value = 0; // normalized to 0 if negative
        // value = this.value; // NOT NEEDED — assignment happens after block
    }
}

System.out.println(new Safe(-5).value());  // 0 (normalized)
System.out.println(new Safe(10).value());  // 10

빠른 확인

간결한 생성자에서 컴포넌트 변수를 수정하면 어떻게 되나요?

복습: 간결한 생성자와 검증

핵심 내용:

  • 간결한 생성자는 컴포넌트가 할당되기 전에 실행되므로 명시적인 할당이 필요하지 않습니다
  • 컴포넌트를 검증하거나 정규화하거나 방어적으로 복사할 때 사용합니다
  • 컴포넌트 변수를 수정하여 값을 정규화합니다(소문자 변환, 공백 제거, 복사)
  • 잘못된 데이터에는 IllegalArgumentException을 발생시켜 불변 조건을 적용합니다
  • 변경 가능한 컬렉션 컴포넌트에는 List.copyOf / Set.copyOf를 사용합니다
  • 정식 생성자가 아닌 생성자는 this(...)를 사용하여 canonical 생성자에 위임해야 합니다

자주 묻는 질문

“간결한 생성자와 유효성 검사” 강의는 무료인가요?

네 — “간결한 생성자와 유효성 검사” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“간결한 생성자와 유효성 검사”에서 뭘 배우나요?

데이터 무결성을 보장하도록 간결한 생성자 내부에 유효성 검사 로직을 추가합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“간결한 생성자와 유효성 검사” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 레코드 소개
  2. 간결한 생성자와 유효성 검사
  3. 레코드의 사용자 정의 메서드
  4. 레코드, 클래스, Lombok 비교
← Java Academy(으)로 돌아가기