728x90
반응형

앞 글에서 던진 떡밥, 오늘 회수합니다.
"Virtual Threads가 게임 체인저"라고 했는데, 그래서 어떻게 써야 잘 쓰는 건지 코드와 함께 정리해드립니다.

Virtual Threads(가상 스레드)는 Java 21에서 정식 출시된 후 Java 24의 JEP 491로 가장 큰 약점이었던 pinning 문제까지 해결되면서, 2026년 현재 운영 환경 도입의 모든 장벽이 사라진 상태입니다.

이 글은 "개념 설명"이 아니라 "실전 코드 + 함정 회피" 중심입니다. Spring Boot 통합, HikariCP 이슈, ThreadLocal 트랩까지 모두 다룹니다.


✅ TL;DR — 5줄 요약

  • Spring Boot 3.2+에서는 spring.threads.virtual.enabled=true 한 줄이면 끝
  • Virtual Thread는 풀링 금지 — 매 작업마다 새로 만드는 게 정석
  • 동시성 제한은 풀 대신 Semaphore
  • Java 21~23에서 synchronized는 pinning 발생 → Java 24/25부터 해결됨
  • CPU-bound 작업은 여전히 Platform Thread가 유리

🎯 왜 가상 스레드인가? (1분 복습)

기존 방식의 한계

전통적인 Java 스레드는 OS 스레드와 1:1 매핑됩니다. 스레드 하나당 약 1MB의 스택 메모리를 잡아먹어요. 그래서:

  • 동시 요청 1만 개? → 메모리 폭발 💥
  • 해결책으로 나온 게 WebFlux 같은 리액티브 프로그래밍
  • 하지만 코드 스타일이 완전히 달라서 학습 비용 ↑

가상 스레드의 해법

Platform Thread (기존):  1 스레드 = 1 OS 스레드 = 1MB 메모리
Virtual Thread (신규):   수백만 개 가능, 메모리 거의 안 씀

JVM이 자체 스케줄러로 가상 스레드를 carrier(플랫폼) 스레드에 mount/unmount 하면서 관리합니다. "동기 코드처럼 짜되, 리액티브처럼 확장된다"가 핵심이에요.


🛠️ 활용법 1 — 순수 Java로 사용하기

가장 기본적인 방법

// 방법 1: Thread.ofVirtual() 빌더
Thread vt = Thread.ofVirtual()
    .name("my-task")
    .start(() -> {
        System.out.println("가상 스레드 실행 중!");
    });
vt.join();

// 방법 2: 한 줄로 시작
Thread.startVirtualThread(() -> {
    System.out.println("간단 버전");
});

실무에서 쓰는 건 이 패턴 ⭐

// ExecutorService를 가상 스레드로 — 가장 많이 쓰는 패턴
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List<Future<String>> futures = IntStream.range(0, 10_000)
        .mapToObj(i -> executor.submit(() -> {
            // 각 작업이 별도의 가상 스레드에서 실행됨
            return callExternalApi(i);
        }))
        .toList();

    for (var future : futures) {
        System.out.println(future.get());
    }
}
// try-with-resources 종료 시 모든 작업 완료 대기

⚠️ 중요: newVirtualThreadPerTaskExecutor()는 작업마다 새 가상 스레드를 생성합니다. 이게 정상이에요. 풀링하지 마세요!


🌱 활용법 2 — Spring Boot 3.2+에서 사용하기

단 한 줄로 활성화

# application.properties
spring.threads.virtual.enabled=true
# application.yml
spring:
  threads:
    virtual:
      enabled: true

이 한 줄이면 Spring Boot가 알아서 다음 영역에 가상 스레드를 적용합니다.

  • Tomcat/Jetty 웹 요청 처리
  • @Async 메서드 실행
  • @Scheduled 작업
  • WebClient 비동기 요청
  • 기타 내부 TaskExecutor

적용 확인하기

@RestController
@RequestMapping("/thread")
public class ThreadCheckController {

    @GetMapping("/info")
    public String getThreadInfo() {
        Thread current = Thread.currentThread();
        return String.format(
            "Thread: %s, Virtual: %s",
            current.getName(),
            current.isVirtual()
        );
    }
}

호출 결과:

Thread: VirtualThread[#171]/runnable@ForkJoinPool-1-worker-4, Virtual: true

isVirtual()true로 나오면 성공!

환경 요구사항

항목 최소 사양 권장
Java 21 24 이상 (JEP 491 적용)
Spring Boot 3.2 3.3+
Spring Framework 6.1 6.2+

⚙️ 활용법 3 — 세밀한 제어가 필요할 때

전체 적용이 아니라 특정 영역에만 가상 스레드를 쓰고 싶다면:

@Configuration
public class VirtualThreadConfig {

    // Tomcat 요청 처리만 가상 스레드로
    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer() {
        return protocolHandler -> protocolHandler.setExecutor(
            Executors.newVirtualThreadPerTaskExecutor()
        );
    }

    // @Async 메서드만 가상 스레드로
    @Bean
    public AsyncTaskExecutor asyncTaskExecutor() {
        return new TaskExecutorAdapter(
            Executors.newVirtualThreadPerTaskExecutor()
        );
    }

    // 특정 비즈니스 로직 전용
    @Bean
    public ExecutorService emailSenderExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }
}

조건부 활성화도 가능합니다.

@Bean
@ConditionalOnProperty(value = "app.thread-mode", havingValue = "virtual")
public AsyncTaskExecutor virtualThreadExecutor() {
    return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}

⚠️ 함정 1 — Virtual Thread는 절대 풀링하지 마세요

가장 많이 하는 실수입니다.

// ❌ 잘못된 예 — 가상 스레드의 의미를 완전히 무시
ExecutorService bad = Executors.newFixedThreadPool(20,
    Thread.ofVirtual().factory());

// ✅ 올바른 예 — 매 작업마다 새 가상 스레드 생성
ExecutorService good = Executors.newVirtualThreadPerTaskExecutor();

가상 스레드는 생성/소멸이 거의 무료입니다. 풀링은 플랫폼 스레드의 비싼 비용을 아끼려는 패턴이지, 가상 스레드에 적용하면 오히려 손해예요.

그럼 동시성 제한은 어떻게?

다운스트림 서비스 보호를 위해 동시 호출을 제한하고 싶다면 Semaphore를 쓰세요.

public class RateLimitedService {
    // 외부 API 동시 호출을 20개로 제한
    private final Semaphore semaphore = new Semaphore(20);

    public String callExternalApi(String url) throws InterruptedException {
        semaphore.acquire();
        try {
            return httpClient.send(url);
        } finally {
            semaphore.release();
        }
    }
}

// 사용 시 — 가상 스레드는 마음껏 만들어도 됨
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> service.callExternalApi("..."));
    }
}

⚠️ 함정 2 — Pinning 이슈 (Java 21~23에서 발생)

Pinning이 뭔가요?

가상 스레드가 carrier 스레드에 달라붙어서 떨어지지 않는 현상입니다. 이렇게 되면 carrier가 다른 가상 스레드를 처리하지 못해 사실상 풀링 모델로 퇴화해요.

Java 21~23에서의 원인

// ❌ Java 21~23에서 pinning 발생
public synchronized void cachedFetch(String key) {  // synchronized
    var data = database.query(key);  // 블로킹 I/O
    cache.put(key, data);
}

synchronized 블록 안에서 블로킹 I/O가 일어나면 가상 스레드가 carrier에 pinning됩니다.

Java 24/25에서 해결됨 (JEP 491) 🎉

Java 24부터 JEP 491: Synchronize Virtual Threads without Pinning이 적용되어 synchronized로 인한 pinning이 거의 사라졌습니다. JVM이 모니터를 virtual thread 단위로 추적하도록 변경됐어요.

그래서 2026년 현재의 권장사항은:

  • Java 24+ 사용 가능synchronized 그대로 사용해도 OK
  • Java 21~23 사용 → 핫 패스의 synchronizedReentrantLock으로 교체 권장
// Java 21~23용 — pinning 회피 패턴
public class SafeCache {
    private final ReentrantLock lock = new ReentrantLock();

    public Data fetch(String key) {
        lock.lock();
        try {
            return database.query(key);  // pinning 안 일어남
        } finally {
            lock.unlock();
        }
    }
}

아직 pinning이 발생하는 케이스

JEP 491 이후에도 다음 경우엔 여전히 pinning이 발생합니다.

  • 네이티브 메서드 호출 중 블로킹
  • Foreign Function & Memory API 사용 중 블로킹
  • 클래스 초기화 중 블로킹

JFR 이벤트 jdk.VirtualThreadPinned로 모니터링 가능합니다.


⚠️ 함정 3 — HikariCP & ThreadLocal 트랩

Spring Boot에서 가상 스레드를 켰는데 DB 연결이 자꾸 타임아웃난다면 의심할 곳이 여기입니다.

문제 상황

HikariCP는 ThreadLocal로 connection을 캐싱하는데, 가상 스레드는 매번 새로 만들어지므로 캐시 히트율이 0에 수렴합니다. 결과적으로:

HikariPool-1 - Connection is not available, request timed out after 30000ms

해결 방법

1. 커넥션 풀 사이즈 증설

spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.connection-timeout=30000

2. 동시 DB 호출에 Semaphore 적용

@Service
public class UserService {
    private final Semaphore dbSemaphore = new Semaphore(50);  // pool size에 맞춤
    private final UserRepository repository;

    public User findById(Long id) throws InterruptedException {
        dbSemaphore.acquire();
        try {
            return repository.findById(id).orElseThrow();
        } finally {
            dbSemaphore.release();
        }
    }
}

3. JDBC 드라이버 최신 버전 사용

MySQL Connector/J 9.0+, PostgreSQL JDBC 최신 버전은 내부 synchronizedReentrantLock으로 교체해 가상 스레드 친화적으로 개선됐습니다.

<!-- pom.xml -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>9.1.0</version> <!-- 9.0+ 권장 -->
</dependency>

⚠️ 함정 4 — ThreadLocal 의존 코드 주의

ThreadLocal을 캐시처럼 쓰는 코드는 가상 스레드에서 무용지물입니다. 매 요청마다 새 스레드라 캐시 히트가 안 일어나요.

// ❌ 가상 스레드에서 의미 없음
private static final ThreadLocal<DateFormat> dateFormat =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

대안: ScopedValue (Java 21 Preview, Java 25 정식)

Java 25에서 정식화된 ScopedValue를 쓰면 가상 스레드에서도 효율적으로 컨텍스트를 전달할 수 있습니다.

// Java 25+
public class RequestContext {
    public static final ScopedValue<String> USER_ID = ScopedValue.newInstance();

    public void handleRequest(String userId, Runnable task) {
        ScopedValue.where(USER_ID, userId).run(task);
    }

    public void someMethod() {
        String currentUser = USER_ID.get();  // 어디서든 접근
        // ...
    }
}

🚫 가상 스레드를 쓰면 안 되는 경우

만능이 아닙니다. 다음 상황에서는 여전히 Platform Thread가 유리해요.

1. CPU-bound 작업

// ❌ 이런 건 가상 스레드 의미 없음
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> {
        // 행렬 곱셈, 이미지 처리, 암호화 등
        return computePrime(1_000_000);  // 순수 CPU 연산
    });
}

// ✅ CPU-bound는 ForkJoinPool 또는 코어 수만큼의 플랫폼 스레드
ExecutorService cpuPool = Executors.newFixedThreadPool(
    Runtime.getRuntime().availableProcessors()
);

가상 스레드의 강점은 블로킹 I/O 처리입니다. CPU 작업은 어차피 코어 수만큼만 동시 실행되니 이득이 없어요.

2. 짧고 동기적인 작업만 하는 경우

레이턴시가 1ms 이하의 매우 짧은 작업이라면 컨텍스트 스위칭 오버헤드가 더 클 수 있습니다.

3. 기존 시스템이 잘 돌아가는 경우

리액티브 코드(WebFlux)가 이미 잘 동작 중이고 성능 이슈가 없다면, 굳이 다 갈아엎을 필요는 없습니다.


📊 모니터링 & 디버깅

Pinning 감지

# Java 21~23: 시스템 프로퍼티
-Djdk.tracePinnedThreads=full

# Java 24+: JFR 이벤트로
java -XX:StartFlightRecording=filename=app.jfr,settings=profile MyApp

가상 스레드 사용 여부 확인

@Component
public class ThreadMonitor {

    @EventListener(ApplicationReadyEvent.class)
    public void logThreadInfo() {
        log.info("Available processors: {}",
            Runtime.getRuntime().availableProcessors());
        log.info("Default Virtual Thread Scheduler parallelism: {}",
            System.getProperty("jdk.virtualThreadScheduler.parallelism"));
    }
}

Spring Boot Actuator 활용

management.endpoints.web.exposure.include=health,metrics,threaddump

/actuator/threaddump에서 가상 스레드와 플랫폼 스레드를 모두 확인할 수 있습니다.


🚀 실전 마이그레이션 체크리스트

기존 Spring Boot 프로젝트에 가상 스레드를 적용하려면 이 순서로 진행하세요.

  • Java 24 이상으로 업그레이드 (또는 최소 Java 21)
  • Spring Boot 3.2 이상으로 업그레이드
  • JDBC 드라이버를 가상 스레드 친화 버전으로 업데이트 (MySQL 9.0+, pgjdbc 최신)
  • HikariCP maximum-pool-size 재검토 (필요 시 증설)
  • ThreadLocal 사용 코드 점검 — 캐시 용도라면 ScopedValue 또는 다른 방식으로 교체
  • 핫 패스의 synchronized 블록에서 블로킹 호출 여부 점검 (Java 21~23인 경우)
  • 스테이징 환경에서 부하 테스트 후 프로덕션 적용
  • application.propertiesspring.threads.virtual.enabled=true 추가
  • 프로덕션 적용 후 JFR/Actuator로 pinning 모니터링

✍️ 결론

Virtual Threads는 더 이상 "신기술 한번 써보자" 단계가 아닙니다. JEP 491로 마지막 장벽까지 사라진 2026년 현재, 다음과 같은 워크로드라면 도입을 진지하게 고민할 시점이에요.

  • 외부 API를 많이 호출하는 백엔드
  • 데이터베이스 I/O가 주요 병목인 애플리케이션
  • 동시 접속자가 많은 웹 서비스
  • 마이크로서비스 간 통신이 빈번한 시스템

리액티브 프로그래밍의 학습 비용 없이도 수만 동시 요청을 처리하는 백엔드를 만들 수 있다는 것, 이게 가상 스레드의 진짜 가치입니다.


📌 핵심 요약 (TL;DR)

🔹 활성화: Spring Boot 3.2+에서 spring.threads.virtual.enabled=true 한 줄
🔹 풀링 금지: newVirtualThreadPerTaskExecutor() 사용, 동시성 제한은 Semaphore
🔹 Pinning: Java 24+에서 JEP 491로 거의 해결됨, Java 21~23은 ReentrantLock 권장
🔹 HikariCP: pool size 재조정 + 최신 JDBC 드라이버 사용
🔹 CPU-bound 작업: 여전히 플랫폼 스레드가 정답


👉 다음 글 예고

다음 편에서는 Java 25에서 정식 출시된 Structured Concurrency 활용법을 다룰게요. 가상 스레드와 함께 쓰면 동시 작업의 생명주기를 깔끔하게 관리할 수 있습니다.

도움이 되셨다면 공감 + 댓글 부탁드려요 😊

 

728x90
반응형
728x90
반응형

백엔드 개발자라면 한 번쯤 고민해본 그 질문
"지금 프로젝트, 자바 몇으로 시작해야 안 망할까?"

2026년 4월 기준, 자바 생태계는 또 한 번 큰 변화를 맞았습니다. 작년 9월에 Java 25 LTS가 나왔고, 한 달 전인 3월엔 Java 26까지 출시됐죠. 그런데 현실은? 아직도 많은 기업이 Java 8, 11을 쓰고 있습니다.

이 글에서는 2026년 현재 자바 버전별 점유율, 특징, 그리고 상황별 추천 버전까지 한 방에 정리해드립니다.


✅ 한눈에 보는 자바 버전 요약 (2026년 기준)

순위 버전 출시 상태 한줄평
🥇 Java 21 LTS 2023.09 현역 메인 신규 프로젝트의 안전한 기본값
🥈 Java 17 LTS 2021.09 엔터프라이즈 표준 Spring Boot 3.x의 최소 사양
🥉 Java 11 LTS 2018.09 레거시 강자 아직도 많은 기업이 운영 중
4위 Java 8 LTS 2014.03 고인물 공공/금융권에 여전히 잔존
🆕 Java 25 LTS 2025.09 최신 LTS 그린필드 프로젝트라면 도전해볼 만
🧪 Java 26 2026.03 비-LTS 6개월 후 지원 종료, 실험용

💡 TIP: LTS(Long-Term Support)는 오라클이 8년간 지원하는 안정 버전이에요. 비-LTS는 6개월짜리 단명 버전이라 실서비스엔 부적합합니다.


📊 진짜 현실: 2025~2026년 자바 점유율 데이터

귀를 의심하실 수도 있는데, 현실은 생각보다 보수적입니다.

  • New Relic 2024년 자바 생태계 리포트: 응답자의 약 2/3가 Java 11 이하 사용 중
  • Azul 2025년 State of Java Survey: 19%가 아직도 Java 6 또는 7을 운영 중
  • JRebel 2024년 리포트: 업그레이드 사유 1위는 신기능(19%)이 아닌 LTS 지원(25%)과 보안(24%)

즉, 대부분의 기업은 "신기능이 좋아서"가 아니라 "지원이 끝나서 어쩔 수 없이" 업그레이드합니다. 트렌드와 현실의 격차가 꽤 크다는 점, 꼭 기억해두세요.


🥇 Java 21 LTS — 2026년 현재의 진짜 메인스트림

출시: 2023년 9월
프리미어 지원: 2028년 9월까지
한 마디로: "지금 신규 프로젝트의 가장 안전한 선택"

왜 Java 21이 메인인가?

  • Virtual Threads (가상 스레드) 정식 도입 → 동시성 처리 패러다임이 바뀜
  • Pattern Matching, Record Patterns, Sealed Classes 등 모던 문법 완성
  • Spring Boot 3.2+, Quarkus, Micronaut 등 주요 프레임워크 완벽 지원
  • 출시 2년 차로 안정성 검증 완료

Virtual Threads가 왜 게임 체인저인가?

기존엔 동시 요청 1만 개 처리하려면 WebFlux 같은 리액티브 프로그래밍을 써야 했습니다. 코드 스타일 자체가 완전히 달라져서 학습 곡선이 가팔랐죠.

가상 스레드는 평소처럼 동기 코드로 작성하면서도 수만 개 동시 요청을 처리할 수 있게 해줍니다. 즉, "리액티브 세금"을 안 내도 되는 거죠.

// Java 21 가상 스레드 예시
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            // 각 작업이 가상 스레드에서 실행됨
            return fetchUserData(i);
        });
    });
}

🥈 Java 17 LTS — 엔터프라이즈의 든든한 허리

출시: 2021년 9월
프리미어 지원: 2026년 9월까지 (⚠️ 올해 종료!)
한 마디로: "지금 운영하는 시스템의 표준, 하지만 곧 업그레이드 시점"

Java 17의 역사적 의미

Spring Boot 3.0부터 최소 요구사항이 Java 17로 올라갔습니다. 이게 결정적이었어요. 수많은 기업이 Java 8/11에서 17로 점프한 이유가 바로 여기 있습니다.

주요 특징

  • Sealed Classes — 상속 계층 봉인
  • Pattern Matching for instanceof — 타입 체크 + 캐스팅 통합
  • Records — 데이터 클래스 보일러플레이트 제거
  • Text Blocks — 멀티라인 문자열 깔끔하게
// Records 예시
public record User(Long id, String name, String email) {}

// Pattern Matching 예시
if (obj instanceof User user && user.id() > 0) {
    System.out.println(user.name());
}

⚠️ 주의사항

오라클 프리미어 지원이 2026년 9월에 종료됩니다. 운영 중인 시스템이라면 슬슬 Java 21 또는 25로 마이그레이션 계획을 세워야 할 시점이에요.


🥉 Java 11 LTS — 아직 살아있는 국민템

출시: 2018년 9월
프리미어 지원: 2026년 9월까지
한 마디로: "현실의 표준, 하지만 슬슬 빠질 시간"

대기업, SI, 공공기관에서 가장 많이 쓰는 버전이 여전히 Java 11입니다. 모듈 시스템, var 키워드, HTTP Client API 등 8 대비 충분히 모던하면서도 안정성이 검증돼 있죠.

하지만 2026년 9월 이후엔 OTN 라이선스로 전환되어 상용 환경에서 무료 사용이 어려워집니다. 마이그레이션 시점이 다가오고 있다는 거예요.


🗿 Java 8 LTS — 사라지지 않는 그분

출시: 2014년 3월
한 마디로: "공공/금융권의 영원한 표준?"

람다, 스트림, Optional을 도입해 자바를 한 단계 끌어올린 역사적 버전이지만, 출시 12년이 지난 지금도 현역입니다. 특히 다음 환경에서 여전히 강세:

  • 공공기관 SI 프로젝트 (제안서에 "Java 8 기준"이 박혀 있는 경우)
  • 금융권 코어뱅킹 시스템
  • 오래된 레거시 운영 환경

다만 새로 시작하는 프로젝트에 Java 8을 선택하는 건 기술 부채를 미리 쌓는 행위입니다. 피할 수 있다면 무조건 피하세요.


🆕 Java 25 LTS — 최신 LTS, 도전해볼 가치 있나?

출시: 2025년 9월
프리미어 지원: 2030년 9월까지
한 마디로: "그린필드 프로젝트라면 적극 고려"

Java 25의 핵심 기능

  • 간소화된 main 메서드public static void main(String[] args) 보일러플레이트 제거
  • Structured Concurrency 정식화 — 동시 작업의 생명주기 관리
  • Scoped Values — ThreadLocal의 현대적 대체재
  • Module Import Declarationsimport module java.base 한 줄로 끝
  • Compact Source Files — 스크립트성 자바 코드 작성이 진짜 쉬워짐
// Java 25 — 이것도 유효한 자바 코드입니다
void main() {
    System.out.println("Hello, Java 25!");
}

도입 권장 시점

  • 2026년 신규 프로젝트로 시작하는 경우
  • 팀 내 자바 숙련도가 높고 빠른 도입 의지가 있는 경우
  • 프레임워크(Spring Boot 3.4+, Quarkus 3.15+ 등) 호환성 확인 후

🧪 Java 26 (2026.03) — 써도 될까?

출시: 2026년 3월 17일
상태: 비-LTS (6개월 후 지원 종료)

Java 26은 HTTP/3 지원, AOT 캐시 개선(ZGC와 호환), G1 처리량 향상 등 의미 있는 개선이 있지만, 실서비스 도입은 비추천입니다. 6개월 후 Java 27이 나오면 지원이 끊기기 때문이죠.

미리 신기능을 테스트해보고 싶은 개발자나 오픈소스 프로젝트에서만 사용하세요.


💼 라이선스 이슈 — 절대 놓치면 안 되는 부분

2023년 오라클이 자바 라이선스를 CPU 단위에서 직원 수 단위(Employee 기반)로 바꾸면서 비용이 폭등했습니다. 그 결과:

  • 오라클 JDK 시장 점유율: 2020년 75% → 2024년 21%로 급락
  • 70% 이상의 기업이 비-오라클 JDK 대안을 검토 중

추천 무료 JDK 배포판

배포판 특징 추천 대상
Eclipse Temurin (구 AdoptOpenJDK) 가장 대중적, 벤더 중립 일반적인 모든 환경
Amazon Corretto AWS 환경 최적화, 무료 LTS AWS 기반 서비스
Azul Zulu 다양한 플랫폼 지원 멀티 플랫폼 운영
BellSoft Liberica 임베디드/JavaFX 포함 데스크톱/IoT
SAP Machine SAP 환경 최적화 SAP 통합 시스템

💡 오라클 JDK 라이선스 비용이 부담된다면 Eclipse Temurin이 가장 무난한 선택입니다.


🎯 상황별 자바 버전 추천 가이드

✅ 신규 프로젝트를 시작한다면

  • 첫 번째 선택: Java 21 LTS (안정성 + 가상 스레드)
  • 공격적 선택: Java 25 LTS (최신 LTS, 5년 더 긴 지원)

✅ Spring Boot 사용 시

  • Spring Boot 3.x → Java 17 이상 필수 (21 권장)
  • Spring Boot 4.0+ (예정) → Java 21 이상 필수

✅ 레거시 시스템 유지보수

  • 기존 Java 8 → 현행 유지하되, 점진적으로 11/17 마이그레이션 계획
  • 기존 Java 11 → 2026년 9월 지원 종료 대비 17 또는 21로 업그레이드 시작

✅ 공공/금융 SI 프로젝트

  • 발주처 요구사항 확인이 최우선
  • 일반적으로 Java 8 또는 11이 기준
  • 자체 판단 가능하다면 17 제안 시도

✅ 마이크로서비스 / 클라우드 네이티브

  • Java 21 + Virtual Threads 조합 강력 추천
  • 컨테이너 환경 → AOT 컴파일 고려 시 GraalVM Native Image도 검토

✍️ 결론 — 그래서 뭘 쓰라고?

복잡하게 생각할 것 없이 이렇게 정리됩니다.

🎯 2026년 현재의 정답

  • 신규 프로젝트 → Java 21 LTS (가장 안전한 메인스트림)
  • 보수적 환경 → Java 17 LTS (단, 2026년 9월 지원 종료 주의)
  • 그린필드 + 도전 → Java 25 LTS
  • 레거시 운영 → 현재 버전 유지 + 마이그레이션 계획 수립

자바는 더 이상 "느리고 무거운 언어"가 아닙니다. Virtual Threads로 동시성 문제를 해결했고, Project Amber로 보일러플레이트를 줄였고, Project Leyden으로 시작 시간까지 개선되고 있어요. 2026년의 자바는 그 어느 때보다 모던합니다.


📌 핵심 요약 (TL;DR)

  • 🎖 현재의 표준: Java 21 LTS (2023년 출시, 2028년까지 지원)
  • 🆕 최신 LTS: Java 25 (2025년 출시, 2030년까지 지원)
  • ⚠️ 곧 EOL: Java 11, 17 — 2026년 9월 프리미어 지원 종료
  • 💰 라이선스: 오라클 JDK 대신 Temurin, Corretto 등 무료 대안 추천
  • 🚀 Virtual Threads: 자바 동시성의 게임 체인저, Java 21부터 사용 가능

👉 이 글이 도움 되셨다면 공감과 댓글 부탁드려요 😊

다음 글에서는 Virtual Threads 실전 활용법을 자세히 다뤄볼게요!


태그: #Java #Java21 #Java25 #자바버전 #JavaLTS #SpringBoot #백엔드개발 #VirtualThreads #자바개발자 #JDK #Temurin #Corretto #2026개발트렌드

728x90
반응형
728x90
반응형

Spring Boot 4.0 핵심 변경점 정리 — Java 25 지원부터 API 버저닝까지

2025년 11월, Spring Boot 4.0이 정식 출시됐다. 단순한 마이너 업데이트가 아니라 Spring Boot의 다음 세대를 여는 메이저 릴리즈다. Spring Framework 7과 Jakarta EE 11 위에 새로 올라섰고, 코드베이스 자체가 재구성됐다. 3.x를 잘 쓰고 있다면 굳이 지금 올려야 하나 고민될 수도 있는데, 결론부터 말하면 지금 당장 옮길 필요는 없지만, 신규 프로젝트라면 4.x로 시작하는 게 합리적이다.

이 글에서는 실무 관점에서 가장 중요한 변경점만 추려서 정리한다.


한눈에 보는 핵심 변경점

  • Spring Framework 7 / Jakarta EE 11 기반으로 전환
  • Java 25에 대한 1급 지원, 단 Java 17 호환은 유지
  • 거대한 auto-configure JAR을 작은 단위 모듈로 분해
  • JSpecify 기반 null 안전성을 표준으로 채택
  • API 버저닝 내장 지원 (@GetMapping(version = "1.0"))
  • 인터페이스로 HTTP 클라이언트를 만드는 HTTP Service Clients
  • Jackson 3 기본 채택 (패키지 경로 변경)
  • Gradle 9 빌드 지원
  • GraalVM 25+ 네이티브 이미지 요구사항 상향

출시 배경 — 왜 4.0인가

Spring Boot 3.x는 2022년 말 출시된 이후 약 3년 동안 안정적인 LTS 라인으로 쓰였다. 그동안 Java는 17 → 21 → 25로 두 번의 LTS를 거쳤고, Jakarta EE도 10에서 11로 올라갔다. 무엇보다 Spring Framework 7이 등장하면서, Boot 쪽도 그에 맞게 베이스라인을 끌어올릴 시점이 됐다.

Spring 팀이 이번 릴리즈에서 강조하는 키워드는 두 개다. 모듈화null 안전성. 둘 다 *"이미 동작하는 앱"* 보다는 *"앞으로 만들어질 앱"* 을 위한 변화에 가깝다. 즉, 4.0은 화려한 신기능이라기보다 앞으로 5년치 토대를 다시 까는 작업이라고 보는 편이 정확하다.


1. 코드베이스 모듈화 — 거대한 가방을 여러 개의 작은 가방으로

3.x 시절에는 spring-boot-autoconfigure라는 거대한 JAR 하나가 거의 모든 자동 설정을 들고 있었다. WebMVC만 쓰는 프로젝트에서도 LDAP, Neo4j, Quartz 같은 자동 설정 클래스가 클래스패스에 함께 끌려왔다.

Spring Boot 4.0은 이 구조를 깨고, 각 기술별로 작고 집중된 JAR로 쪼갰다. 예를 들어 spring-boot-starter-webmvc를 추가하면 WebMVC 관련 자동 설정만 따라온다. 그 결과 다음과 같은 효과가 있다.

  • 시작 시간 단축: 클래스패스 스캔 대상이 줄어든다
  • 메모리 사용 감소: 불필요한 자동 설정이 안 올라온다
  • 디버깅 편의성: 어디서 어떤 자동 설정이 들어왔는지 추적이 쉬워진다

다만 모듈이 잘게 쪼개진 만큼, starter POM을 쓰지 않고 직접 의존성을 관리하던 프로젝트는 의존성 누락 이슈가 발생할 수 있다. 마이그레이션 가이드에서도 이 부분을 가장 먼저 강조한다.


2. Java 25 1급 지원, Java 17 호환 유지

Spring Boot 4.0의 최소 요구 Java 버전은 여전히 Java 17이다. 다만 권장은 Java 25 LTS다.

Spring Boot 4.0 requires Java 17 or newer, with first-class support for Java 25.

여기서 "1급 지원(first-class support)"이 의미하는 바는, 단순히 Java 25에서 동작한다는 게 아니라 Virtual Threads, Pattern Matching, Sequenced Collections, 향상된 GC 같은 최신 JVM 기능을 프레임워크 차원에서 적극 활용한다는 뜻이다.

네이티브 이미지를 쓰려면 이야기가 좀 다르다. GraalVM 25 이상이 필요하다. 이전 GraalVM 23/24에서 빌드되던 네이티브 이미지가 그대로 빌드되지 않을 수 있으니, 네이티브를 쓰는 팀은 빌드 파이프라인을 먼저 점검해야 한다.


3. JSpecify로 표준화된 null 안전성

이번 릴리즈에서 가장 조용하지만 가장 영향력이 큰 변화다.

기존 Spring 코드에서 null 처리는 사실상 컨벤션에 의존했다. @Nullable 같은 애노테이션이 있긴 했지만, 어디 패키지의 어떤 애노테이션을 쓰느냐가 라이브러리마다 달랐다(Spring, JetBrains, Checker Framework, FindBugs…). IDE는 이걸 어찌어찌 통합해서 표시해줬지만, 일관성은 부족했다.

Spring Boot 4.0은 JSpecify를 표준 null 안전성 라이브러리로 채택했다. JSpecify는 Google, JetBrains, Spring 팀이 함께 합의한 일종의 표준 신호 체계다. 핵심 애노테이션은 다음과 같다.

  • @Nullable — 이 값은 null일 수 있다
  • @NonNull — 이 값은 절대 null이 아니다
  • @NullMarked — 이 패키지/클래스의 모든 참조는 기본 non-null이다

package-info.java@NullMarked를 한 번만 붙이면 패키지 안의 모든 참조 타입이 기본 non-null로 간주되고, null이 들어올 수 있는 곳만 @Nullable로 표시하면 된다. 코드 가독성이 명확해지고, IntelliJ나 NullAway 같은 정적 분석 도구가 빌드 단계에서 더 잘 잡아낸다.

// package-info.java
@NullMarked
package com.kraft.member;

import org.jspecify.annotations.NullMarked;
public Member findById(Long id) { ... }       // 절대 null 아님
public @Nullable Member findByEmail(String email) { ... }  // null 가능

기존 코드에 즉시 영향을 주진 않지만, 새로 작성하는 코드에는 처음부터 적용하는 게 좋다.


4. API 버저닝 — 이제 컨트롤러 한 줄로 처리

API 버저닝은 그동안 모든 팀이 각자의 방식으로 고생하던 영역이었다. URL 경로(/v1/...)에 박아 넣거나, 커스텀 헤더로 분기하거나, 미디어 타입으로 분기하거나. 어느 방식을 택하든 컨트롤러를 직접 갈라야 했고, 전략을 바꾸면 모든 컨트롤러를 다시 손대야 했다.

Spring Boot 4.0에서는 @RequestMapping 계열 애노테이션에 version 속성이 추가됐다.

@RestController
@RequestMapping("/api/posts")
public class PostController {

    @GetMapping(version = "1.0")
    public PostV1Response getPostsV1() { ... }

    @GetMapping(version = "2.0")
    public PostV2Response getPostsV2() { ... }
}

버전 분기 전략(URL, 헤더, 미디어 타입)은 설정에서 한 번만 정의하면 되고, 컨트롤러는 버전 번호만 신경 쓰면 된다. 전략을 나중에 바꿔도 컨트롤러 코드는 그대로 둘 수 있다는 점이 핵심이다.


5. HTTP Service Clients — 인터페이스만 선언하면 끝

외부 API를 호출할 때 RestTemplate이나 WebClient로 매번 코드를 짜는 대신, 인터페이스만 선언하고 구현체를 자동 생성하는 방식이 정식 자동 설정으로 들어왔다.

@HttpExchange(url = "https://api.example.com")
public interface WeatherClient {

    @GetExchange("/weather/{city}")
    WeatherResponse getWeather(@PathVariable String city);

    @PostExchange("/alerts")
    AlertResponse createAlert(@RequestBody AlertRequest request);
}

Feign이나 Retrofit을 써 본 사람이라면 친숙할 것이다. 차이는 Spring 자체에 통합됐다는 점이다. 별도 라이브러리 없이 @HttpExchange만 붙이면 Spring Boot가 알아서 구현체를 만들어 빈으로 등록한다. 4.x의 Spring Cloud Commons 5.0에서는 여기에 Circuit Breaker, 로드밸런싱(lb:// 스킴) 같은 기능까지 선언적으로 얹을 수 있다.

마이크로서비스 환경이 아니더라도, 외부 API를 호출하는 곳이 많은 프로젝트라면 코드량이 눈에 띄게 줄어든다.


6. Jackson 3 — 패키지 경로가 바뀐다

Spring Boot 4.0은 Jackson 3을 기본 JSON 라이브러리로 채택했다. 가장 눈에 띄는 변경은 패키지/그룹 ID다.

  • com.fasterxml.jackson.*tools.jackson.* 로 이동
  • 단, jackson-annotations 모듈은 호환성 때문에 com.fasterxml.jackson.core 그룹 ID와 com.fasterxml.jackson.annotation 패키지를 유지

직접 ObjectMapper를 import해서 쓰는 코드가 있다면 패키지 경로를 바꿔야 한다. 다만 Jackson 2를 요구하는 외부 라이브러리도 여전히 많기 때문에, Jackson 2와 3을 한 프로젝트에서 공존시킬 수 있도록 의존성 관리는 계속 제공된다.


그 외 주목할 변화

위에 적은 6가지 외에도 실무에서 한 번씩 만나게 되는 변경이 많다.

  • OpenTelemetry Starter 신설: spring-boot-starter-opentelemetry 추가만으로 OTLP 메트릭/트레이스를 내보낼 수 있다
  • Gradle 9 지원: Gradle 8.14 이상이면 둘 다 지원
  • Kotlin 2.2+ 요구: Kotlin 프로젝트는 빌드 환경부터 점검 필요
  • 클래식 uber-jar 로더 제거: 빌드 스크립트에서 관련 설정을 떼야 한다
  • Spring Retry 의존성 제거: 재시도 로직은 spring-core의 새 retry API로 이동
  • Testcontainers 모듈 prefix 정리: 모든 모듈이 testcontainers- 접두어로 통일
  • SSL 인증서 만료 모니터링 강화: expiringChains 항목 추가로 만료 임박 인증서를 health 응답에서 분리해 보여줌

3.x → 4.x 업그레이드, 무엇을 점검해야 하나

기존 프로젝트를 올리려면 다음 순서로 접근하는 게 안전하다.

  1. 3.5로 먼저 올린다: 3.x 안에서 deprecated 된 API를 모두 정리한 뒤 4.x로 점프
  2. Java 17 이상인지 확인: 11이나 8을 쓰고 있다면 자바 마이그레이션이 선행
  3. starter POM 사용 여부 점검: 직접 의존성 관리를 하고 있다면 누락 모듈 식별이 가장 먼저
  4. Jackson 사용처 점검: com.fasterxml.jackson.* 직접 import 여부, 커스텀 직렬화 코드 확인
  5. 네이티브 이미지 쓰는 경우 GraalVM 25로 업그레이드
  6. JSpecify 기반 정적 분석 도입 검토: 즉시 적용은 부담스러우면 신규 패키지부터

운영 중인 서비스를 무조건 빨리 올릴 이유는 없다. 3.x도 한동안 유지보수가 계속될 예정이고, 4.0의 진가는 신규 코드를 쓸 때 더 잘 드러난다.


마무리 — 4.0을 어떻게 받아들여야 할까

Spring Boot 4.0은 눈에 확 띄는 신기능 보다는 앞으로 5년의 토대를 다시 까는 릴리즈에 가깝다. 모듈화로 슬림해진 런타임, JSpecify로 표준화된 null 안전성, 인터페이스 기반 HTTP 클라이언트, 내장 API 버저닝 — 이 네 가지만으로도 신규 프로젝트라면 4.x로 시작할 명분이 충분하다.

반면 운영 중인 3.x 서비스는 서두를 필요가 없다. 3.5에서 deprecated 정리부터 차근차근 하면서, 분기점이 잡힐 때 옮기면 된다. 진짜로 챙겨야 할 건 새로 짜는 코드부터 4.x 스타일로 쓰는 것이다. JSpecify 애노테이션, 인터페이스 기반 HTTP 클라이언트, API 버저닝 attribute 같은 건 지금 손에 익혀두면 마이그레이션 시점에 부담이 훨씬 줄어든다.

Spring Boot 4.0은 이미 동작하는 앱을 깨러 온 게 아니라, 앞으로 만들 앱을 더 잘 만들게 하러 온 릴리즈다. 그 관점으로 보면 어디서부터 손을 댈지 답이 보인다.


참고 자료

  • Spring Boot 4.0 Release Notes (spring-projects/spring-boot Wiki)
  • Spring Boot 4.0 Migration Guide
  • Spring 공식 블로그 — Spring Boot 4.0.0 available now (2025.11.20)
  • JetBrains Blog — Spring Boot 4: Leaner, Safer Apps and a New Kotlin Baseline

#SpringBoot4 #SpringBoot #Java25 #JSpecify #APIVersioning #HTTPInterface #Jackson3 #SpringFramework7 #JakartaEE11 #백엔드 #Spring업그레이드 #자바개발


728x90
반응형
728x90
반응형

RESTful API는 웹 개발자라면 반드시 알아야 할 핵심 기술입니다.


✅ REST란 무엇인가요?

**REST(Representational State Transfer)**는 2000년에 로이 필딩(Roy Fielding)이 논문에서 처음 제안한 아키텍처 스타일입니다.
쉽게 말해, 웹에서 자원을 HTTP 방식으로 다루는 표준화된 방법이라고 생각하시면 됩니다.


📌 REST의 6가지 핵심 제약조건

제약 조건설명
1. 클라이언트-서버 구조 역할을 분리하여 유지보수성과 확장성 향상
2. 무상태(Stateless) 요청 간 서버는 클라이언트 상태를 저장하지 않음
3. 캐시 처리 가능 응답 데이터는 캐싱될 수 있어야 함
4. 계층화 시스템 중간 서버를 통해 확장 구조 구성 가능
5. 인터페이스 일관성 URI, HTTP 메서드 등 통일된 사용법 준수
6. 코드 온 디맨드 (선택사항) 서버가 클라이언트에 스크립트 전송 가능
 

🧭 RESTful API란?

REST 원칙을 따르는 API를 말합니다. 즉, HTTP 메서드와 URL을 이용해 자원을 CRUD 방식으로 처리하는 것이죠.


🚀 RESTful API 예제

가상의 블로그 게시글(Post)에 대한 API를 설계한다고 가정해볼게요.

기능HTTP 메서드URI설명
목록 조회 GET /posts 모든 게시글 조회
단일 조회 GET /posts/{id} 특정 게시글 조회
작성 POST /posts 게시글 작성
수정 PUT /posts/{id} 게시글 전체 수정
삭제 DELETE /posts/{id} 게시글 삭제
 

🔄 RESTful 하지 않은 API 예시

GET /getPosts
POST /createPost
  • ❌ 이런 방식은 RESTful하지 않습니다.
  • ✅ RESTful 방식은 명사 중심 URI와 HTTP 메서드를 적절히 사용하는 것이 핵심입니다.

💡 RESTful API 설계 시 팁

  • URI에는 동사 대신 명사 사용
  • URI는 소문자 사용
  • 언더스코어(_)보단 하이픈(-) 사용 권장
  • 응답 상태 코드 (200, 201, 400, 404 등) 정확히 설정
  • 예외 응답은 JSON 형태로 일관되게 처리

🛠️ RESTful API 구현 프레임워크

  • Spring Boot (Java)
  • Express (Node.js)
  • Flask / Django REST Framework (Python)
  • FastAPI (Python, 최신 트렌드)
  • Laravel (PHP)
728x90
반응형
728x90
반응형

1. 스프링의 시작은 책 한 권에서

스프링(Spring)은 단순한 프레임워크 그 이상입니다. 그 시작은 2002년, Rod Johnson이 집필한 『Expert One-on-One J2EE Design and Development』라는 책에서 등장한 소스 코드에서 비롯됩니다. 당시 **EJB(Enterprise JavaBean)**는 무거운 구조와 복잡성으로 인해 '겨울'과도 같았죠. Rod는 이를 비판하며 '단순함'을 추구했고, 그 철학이 '봄(Spring)'이라는 이름으로 이어진 것입니다.

❝ EJB의 겨울을 지나, 스프링의 봄이 온다 ❞

스프링은 2003년 6월에 처음 세상에 공개되었고, 오픈소스 프로젝트로서 Apache License 2.0을 따릅니다. 2022년 11월 기준으로는 6.0.0 버전까지 출시되어 있으며, 주요 기능은 이제 Spring Boot를 중심으로 구현되고 있습니다.

2. 왜 스프링이 중요한가?

  • POJO 기반: 복잡한 컴포넌트 없이 순수한 Java 객체로 개발이 가능.
  • DI/IoC 컨테이너: 객체의 생성과 의존성 관리를 프레임워크가 자동으로 처리.
  • 모듈화: AOP(Aspect-Oriented Programming)를 통해 핵심 로직과 부가 기능을 깔끔히 분리.
  • JVM 기반 호환성: Java는 물론 Kotlin 등 다양한 언어와도 호환.
  • 강력한 생태계: Spring MVC, Spring Data, Spring Security, Spring Cloud 등 수많은 확장 가능성.
  • 전자정부 프레임워크 기반: 국내 공공기관 시스템의 표준으로도 사용.

3. 스프링을 제대로 활용하려면?

  • Spring Boot로 시작하면 빠르고 간단한 설정이 가능
  • IntelliJ IDEA는 Spring에 최적화된 대표 IDE (JetBrains 공식 지원)
  • 테스트/배포 자동화까지 생각한다면 Swagger, Docker, GitHub Actions와 연동 추천

✍️ 요약문

스프링은 복잡했던 Java EE의 시대에 ‘단순함’이라는 가치를 제시하며 등장한 웹 프레임워크입니다. Rod Johnson의 철학에서 출발한 이 프로젝트는 이제 전 세계 수많은 기업과 공공기관이 사용하는 강력한 인프라가 되었습니다. 만약 Java 기반 백엔드를 시작하고 싶다면, Spring은 그 자체로도 가장 강력한 출발점입니다.

728x90
반응형
728x90
반응형

자바 개발자라면 꼭 알고 있어야 하는 GC(Garbage Collector)!
객체는 언제 메모리에서 사라지고, 애플리케이션이 왜 가끔씩 멈추는지 궁금하셨나요?
아래 이미지와 함께 GC의 작동 구조를 쉽게 설명해드립니다.


🔍 GC 개념 요약

**Garbage Collector(GC)**는 사용하지 않는 객체를 힙 메모리에서 자동으로 제거하는 JVM의 기능입니다.
메모리 누수를 방지하고 개발자가 직접 메모리를 관리하지 않아도 되게 해주는 아주 고마운 기능이죠.


🧠 Java 힙 메모리 구조

아래 이미지는 자바 힙(Heap) 메모리의 구성과 객체 이동 과정을 시각적으로 표현한 구조입니다.

✅ 설명:

  • 객체는 먼저 Eden 영역에 생성됩니다.
  • GC(Minor GC)가 발생하면 Eden → Survivor1 → Survivor2를 오가며 살아남습니다.
  • 여러 번 살아남으면 Old 영역으로 이동합니다.
  • Old가 꽉 차면 **Major GC (또는 Full GC)**가 발생합니다.
  • Permanent(또는 Java 8 이후 MetaSpace)는 클래스 정보 등 JVM 메타데이터를 저장하는 공간입니다.

💥 GC의 종류

GC 종류대상 영역특징
🧹 Minor GC Young Generation (Eden + Survivor) 빠르고 자주 발생함
🧹 Major GC Old Generation 느리고 멈춤(STW)이 발생함
🧹 Full GC 전체 힙 영역 Major GC 포함 + 기타 리소스 정리
 

🛑 STW(Stop The World)란?

GC를 수행하는 동안 JVM의 모든 스레드가 멈추는 현상입니다.
특히 Major GC나 Full GC 시 STW가 발생하며 사용자 체감 지연이 생길 수 있습니다.

📌 GC 튜닝의 핵심: STW 시간을 최소화하는 것!


⚙ 대표적인 GC 알고리즘

GC 종류특징적합 환경
Serial GC 단일 스레드, 기본 구조 저사양/테스트용
Parallel GC 멀티스레드 병렬 처리 일반 서버, 병렬 CPU
Parallel Old GC Old까지 병렬 처리 고사양 서버
G1 GC Region 단위 처리, STW 최소화 Java 9 이상 대규모 앱
ZGC/Shenandoah 실시간 GC, STW 거의 없음 실시간 처리 시스템
 

🧰 개발 팁 요약

  • 객체는 가능한 빨리 Eden에서 제거되도록 설계하자
  • Old로 가는 객체는 재사용하거나 Pool 처리가 바람직
  • GC 튜닝 시에는 G1 GC, ZGC 등 최신 GC 전략 고려

✅ 마무리

GC는 Java의 강력한 장점 중 하나지만,
그 내부 동작을 모르면 애플리케이션 지연이나 STW 문제를 겪기 쉽습니다.
이제 GC 동작 구조를 알았으니, 메모리 튜닝과 성능 최적화에 자신감이 붙으실 겁니다!

728x90
반응형
728x90
반응형

🗃️ 1. DAO란?

DAO (Data Access Object)
→ 데이터베이스에 접근하는 역할

  • DB와 직접 통신하는 클래스
  • SQL, JPA, Mapper 등을 통해 DB 데이터를 가져오거나 넣는 책임을 가짐
  • 비즈니스 로직과 분리되어 코드의 책임이 명확해짐
public class UserDao {
    public void save(User user) {
        // DB insert 로직
    }

    public User findById(Long id) {
        // DB select 로직
    }
}

📦 2. DTO란?

DTO (Data Transfer Object)
→ 계층 간 데이터 전달용 객체

  • 컨트롤러 ↔ 서비스 ↔ DAO 사이에서 데이터를 전달함
  • 순수 데이터만 포함 (로직 없음)
  • Getter/Setter만 있는 클래스
public class UserDto {
    private String name;
    private String email;
    // getter / setter
}

🧾 실무 예시:

  1. 사용자가 입력한 데이터를 UserDto에 담아 서버로 전송
  2. 서버는 DTO를 받아 DAO를 통해 DB에 저장

🔐 3. VO란?

VO (Value Object)
불변의 값을 나타내는 객체

  • DTO와 비슷하지만 값이 한 번 생성되면 변경 불가
  • setter()가 없고, 생성자로만 값 설정
  • 주로 equals(), hashCode()가 중요
public class Money {
    private final int amount;

    public Money(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }
}

🔍 DAO / DTO / VO 차이 비교표

항목DAODTOVO
역할 DB 접근 담당 계층 간 데이터 전달 불변 값 표현
포함 내용 DB 로직 순수 데이터 불변 데이터
메서드 쿼리/비즈니스 연동 getter/setter getter only
변경 가능성 O O
 

✅ 마무리 요약

  • DAO: DB와 대화하는 친구
  • DTO: 데이터를 옮겨주는 메신저
  • VO: 한 번 정해지면 변하지 않는 값 객체
728x90
반응형
728x90
반응형

📋 본문

오늘은 많은 백엔드/프론트엔드 개발자분들이 헷갈려하는 개념, StatefulStateless의 차이점에 대해 설명드리려 합니다.
서버 아키텍처를 설계할 때 정말 자주 나오는 주제이니, 끝까지 읽어보시면 많은 도움이 되실 거예요!


✅ 1. 정의부터 확실하게!

구분StatefulStateless
의미 서버가 클라이언트 상태(Session, Cookie 등)을 기억함 서버가 클라이언트 상태를 기억하지 않음
연결 방식 지속적인 연결 유지 요청마다 연결 후 즉시 종료
정보 유지 이전 요청 상태 저장 매 요청 시 모든 정보를 포함해야 함
 

✅ 2. 장단점 비교

🔹 Stateful

  • 장점:
    • 로그인 상태 유지 등 기능 구현이 쉬움
    • MMORPG 같은 실시간 상호작용 서비스에 유리
  • 단점:
    • 서버에 리소스 부담
    • 서버 간 세션 공유 어려움 → Scale-out 불리

🔹 Stateless

  • 장점:
    • 서버 확장, 로드밸런싱, 캐시 활용 용이
    • REST API, 클라우드 환경에 적합
  • 단점:
    • 매 요청 시 상태 정보 포함 → 네트워크 자원 사용 ↑
    • 클라이언트 측 부담 ↑

✅ 3. 서비스 예시로 이해하기

서비스 예시적합한 방식
MMORPG 게임 서버 ✅ Stateful
REST API 기반 웹 서비스 ✅ Stateless
IoT 실시간 데이터 수신 서버 ✅ Stateful
블로그, 커머스 등 일반 웹 서버 ✅ Stateless
 

✅ 4. 기술적 이슈 요약

이슈설명
Scale-out Stateful은 세션 동기화 어려움
CSRF 세션 인증 방식의 보안 취약점
CORS 도메인 간 요청 제약 발생 가능
REST API Stateless 원칙 준수 필요
 

✅ 5. 마무리 요약

  • Stateful: 기능 구현은 쉽지만 서버에 부담, 확장성 떨어짐
  • Stateless: 구현 복잡하지만 성능, 확장성, 클라우드에 유리
  • 선택 기준: 서비스의 실시간성/확장성/안정성에 따라 결정!
728x90
반응형

+ Recent posts