Skip to content

Java 17 주요 기능과 실무 적용 ​

이 글은 Connectrip 개발 당시 Java 17을 사용하면서 실제로 활용한 주요 기능들을 정리한다. Java 11부터 17까지의 변경사항 중에서 실무에서 자주 사용하게 된 기능들을 중심으로 작성했다. 현재는 Kotlin을 주로 사용하고 있어서, Java 17 기능들을 Kotlin과 비교한 관점도 함께 다룬다.

Pattern Matching for instanceof ​

Pattern Matching for instanceof는 기존 instanceof 후 명시적 캐스팅 패턴에서 발생하기 쉬운 ClassCastException을 방지하고 코드 안전성을 향상시킨다. 실제 적용 결과 타입 체크와 캐스팅 과정의 실수가 현저히 감소했다.

기존 방식

java
public class PatternMatchingExample {
    public static void main(String[] args) {
        Object obj = "Hello, World!";

        if (obj instanceof String) {
            String str = (String) obj;
            System.out.println(str.toUpperCase());
        }
    }
}

개선된 방식

java
public class PatternMatchingExample {
    public static void main(String[] args) {
        Object obj = "Hello, World!";

        if (obj instanceof String str) {
            // 타입 변환 없이 바로 사용
            System.out.println(str.toUpperCase());
        }
    }
}

API 응답 처리 시 Object 타입 데이터의 타입별 분기 처리에서 특히 효과적이었다. 외부 API에서 받은 다양한 형태의 데이터를 처리하거나, 인터페이스나 추상 클래스를 구현한 여러 타입의 객체를 다룰 때, 그리고 입력 데이터의 타입 확인 후 즉시 해당 타입의 메서드를 호출해야 하는 유효성 검증 과정에서 유용했다. 기존 방식 대비 캐스팅 관련 실수가 감소했고, 코드 가독성도 향상되었다.

Kotlin과의 비교 ​

Kotlin의 smart cast와 비슷한 개념이다. Kotlin에서는 이미 이런 방식이 기본이었다.

kt
fun processData(obj: Any) {
    if (obj is String) {
        // obj는 자동으로 String 타입으로 캐스팅됨
        println(obj.uppercase())
    }
}

Java 17에서 이 기능이 추가되면서 Kotlin과 Java 사이의 문법적 격차가 많이 줄어들었다. 다만 Kotlin의 when 표현식과 결합했을 때의 강력함에는 아직 못 미친다. Kotlin은 패턴 매칭과 when을 조합해서 타입 체크, 값 범위 검사, 조건식을 모두 한 번에 처리할 수 있지만, Java는 아직 instanceof pattern matching이 switch와 완전히 통합되지 않았기 때문이다.

Record - 불변 데이터 클래스의 혁신 ​

Record는 DTO 클래스 작성 시 반복되는 boilerplate 코드 문제를 근본적으로 해결한다. 기존에는 getter, setter, equals, hashCode, toString 등을 수동으로 구현해야 했지만, Record는 이 모든 기능을 자동 생성하면서도 불변성을 보장한다.

java
public record PersonRecord(String name, int age) {
}

위 한 줄로 다음 기능들이 자동 생성된다.

java
public class Main {
    public static void main(String[] args) {
        // 1. 생성자 - 모든 필드를 초기화하는 생성자 자동 생성
        PersonRecord person = new PersonRecord("Taehyeon", 20);

        // 2. 접근자 메서드 - 필드명과 동일한 메서드명 생성
        System.out.println("name: " + person.name());
        System.out.println("age: " + person.age());

        // 3. equals() - 모든 필드 값 비교
        System.out.println("equals(): " +
            person.equals(new PersonRecord("Taehyeon", 20)));

        // 4. hashCode() - 모든 필드 기반 해시 생성
        System.out.println("hashcode(): " +
            (person.hashCode() == new PersonRecord("Taehyeon", 20).hashCode()));

        // 5. toString() - 클래스명[필드명=값] 형식 출력
        System.out.println(person); // PersonRecord[name=Taehyeon, age=20]
    }
}

Record 사용 시 고려사항 ​

Record는 강력하지만 몇 가지 제약사항이 존재한다.

  • 상속 불가: Record는 암시적으로 final이며 extends 사용 불가
  • 불변성: 인스턴스 생성 후 필드 값 변경 불가, 새 인스턴스 생성 필요
  • 인터페이스 구현: 인터페이스 구현은 가능하므로 추상화 설계에 활용
  • 제네릭 지원: public record PersonRecord<T>(T data) {} 형태로 사용 가능

API 응답 객체나 설정 데이터처럼 불변성이 중요한 도메인에서 Record를 활용하면 제약사항이 오히려 설계상 이점으로 작용한다. 코드 가독성과 유지보수성이 동시에 향상되는 효과를 얻었다.

Kotlin data class와의 비교 ​

Kotlin의 data class와 매우 유사하다.

kt
data class PersonData(val name: String, val age: Int)

둘 다 자동으로 equals, hashCode, toString을 생성한다. 하지만 몇 가지 차이점이 있다.

Java: Record

  • 완전한 불변성 (모든 필드가 final)
  • 상속 불가
  • 생성자 파라미터만 필드로 사용 가능

Kotlin: data class

  • var 사용 시 가변성 허용
  • open 클래스 상속 가능
  • copy() 메서드 자동 생성 (불변 객체의 부분 수정 편의성)
  • 컴포넌트 함수 (구조 분해) 지원

JVM 바이트코드 레벨에서는 둘 다 비슷하게 컴파일되지만, Kotlin data class가 더 유연하다. 다만 Java Record의 완전한 불변성은 함수형 프로그래밍 관점에서는 장점이 될 수 있다.

Switch Expression과 Text Block ​

Switch Expression ​

Switch Expression이 도입되면서 기존 switch문의 주요 문제점들이 해결되었다. 특히 break 누락으로 인한 fall-through 버그를 원천적으로 방지하며, 표현식으로 값을 직접 반환할 수 있어 코드 안전성과 가독성이 향상되었다.

java
String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> {
        System.out.println("Processing Wednesday");
        yield "Wednesday";
    }
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6 -> "Saturday";
    case 7 -> "Sunday";
    default -> throw new IllegalArgumentException("Invalid day: " + day);
};

주요 개선점

  • fall-through 방지로 런타임 에러 감소
  • yield 키워드를 통한 복합 로직 처리
  • 표현식으로 직접 사용 가능하여 변수 할당 및 반환값 처리 간소화

Kotlin when과의 비교

Kotlin의 when은 더 강력하다.

kt
val dayName = when (day) {
    1 -> "Monday"
    2 -> "Tuesday"
    in 3..5 -> "Midweek"
    else -> "Weekend"
}

when은 범위 검사, 타입 검사, 조건식 등이 모두 가능하지만, Java switch expression은 아직 값 비교에 제한된다. 하지만 Java도 pattern matching이 계속 발전하고 있어서 앞으로는 더 비슷해질 것 같다.

Text Block ​

Text Block 기능으로 멀티라인 문자열 처리가 크게 개선되었다. JSON, SQL 쿼리, HTML 등의 텍스트 블록을 다룰 때 기존의 문자열 연결이나 StringBuilder 사용 방식 대비 가독성과 유지보수성이 향상되었다.

java
public class TextBlockExample {
    public static void main(String[] args) {
        String textBlock = """
                This is a text block.
                It can span multiple lines.
                Indentation is handled automatically.
                """;

        System.out.println(textBlock);
    }
}

테스트 코드에서 JSON 데이터 작성 시 특히 효과적이었다. 자동 인덴테이션 처리와 이스케이프 문자 불필요로 개발 효율성이 크게 향상되었으며, SQL 쿼리 작성에서도 동일한 이점을 확인할 수 있었다.

Kotlin: raw string과의 비교

kt
val textBlock = """
    This is a raw string.
    It can span multiple lines.
    Indentation needs manual handling.
""".trimIndent()

둘 다 멀티라인 문자열을 지원하지만 차이가 있다.

  • Java Text Block: 인덴테이션 자동 처리
  • Kotlin raw string: trimIndent() 같은 함수로 수동 처리

Kotlin이 먼저 이 기능을 도입했고, Java가 나중에 더 편리한 형태로 구현했다는 느낌이다.

정리하며 ​

Java 17 사용 과정에서 느낀 점은 단순한 문법 개선을 넘어서 개발 패러다임의 변화를 경험할 수 있었다는 것이다. Record와 Pattern Matching은 도메인 모델링을 더 명확하게 만들어주었고, Switch Expression은 조건 처리 로직의 가독성을 크게 향상시켰다.

Kotlin 사용자 관점에서의 Java 17 ​

현재 Kotlin을 주로 사용하는 입장에서 보면, Java 17은 Kotlin의 여러 장점들을 따라잡으려는 노력이 보인다. Pattern matching, Record, Switch expression 등이 모두 Kotlin에서 이미 제공하던 기능들과 유사하다.

하지만 완전히 같지는 않다.

  • Kotlin의 우위: 더 간결한 문법, null safety, 확장 함수, 코루틴 등
  • Java의 장점: 더 큰 생태계, 엔터프라이즈 환경에서의 안정성, 점진적 마이그레이션 가능

JVM에서 두 언어가 완벽하게 호환되므로, 상황에 따라 적절한 언어를 선택할 수 있다는 점이 장점이다. Java 17의 발전으로 두 언어 간 격차가 줄어들어서 팀 내에서 혼용하기도 더 수월해졌다.

성과 측정 ​

Java 17 도입의 구체적 효과는 다음과 같았다.

  • Record: DTO 클래스 작성 시간 약 60% 단축, boilerplate 코드 제거로 코드 리뷰 효율성 향상 (기존 평균 50-80줄에서 30줄 미만 감소)
  • Pattern Matching: 타입 캐스팅 관련 런타임 에러 발생률 감소
  • Text Block: 테스트 코드 내 JSON/SQL 문자열 작성 시간 단축 및 가독성 개선

결론 ​

Java 17은 개발 생산성과 코드 품질 측면에서 의미 있는 개선을 제공한다. 특히 Kotlin과의 기능적 유사성 증가로 JVM 생태계 내에서의 언어 간 호환성이 강화되어, 팀 내 기술 스택 선택의 유연성이 향상되었다. 엔터프라이즈 환경에서 안정성을 유지하면서도 모던 언어의 장점을 점진적으로 도입할 수 있다는 점에서 전략적 가치가 크다.

Released under the MIT License.