Post

Degging (1) - 프로젝트 소개 & 기술 스택 선택기

Degging (1) - 프로젝트 소개 & 기술 스택 선택기

프로젝트 소개

카페를 찾을 때 보통 네이버 지도나 카카오맵을 열어 별점이 높은 곳을 먼저 보게 된다.

그런데 막상 가보면 내 취향과 맞지 않는 경우도 종종 있다.

예를 들어 “분위기 좋은 카페”라는 표현도 사람마다 기준이 다르다.
어떤 사람은 조용한 공간을 떠올리고, 어떤 사람은 힙한 인테리어를 떠올리기 때문이다.

리뷰를 하나씩 읽어보면 어느 정도 분위기를 파악할 수는 있지만
여러 카페를 비교하다 보면 생각보다 시간이 많이 걸린다.

그래서 이런 생각이 들었다.

“사용자의 취향을 기반으로 카페를 추천해주는 서비스가 있으면 좋지 않을까?”

이 생각에서 시작된 프로젝트가 바로 Degging(디깅)이다.

Degging은 단순히 위치 기반으로 카페를 보여주는 서비스가 아니라
사용자의 취향 데이터를 분석해 카페를 추천하는 AI 기반 서비스를 목표로 한다.

데이터가 쌓일수록 사용자의 취향을 더 정확하게 이해하고,
카페를 찾는 시간을 줄여주는 개인화된 카페 및 디저트 큐레이션 서비스를 만들고자 했다.

나는 이 프로젝트에서 백엔드 개발을 담당했고,
이 글에서는 프로젝트를 시작하면서 고민했던 기술 스택 선택 과정을 정리해보려고 한다.


Java 21을 선택한 이유

프로젝트를 시작하면서 가장 먼저 결정해야 했던 것은 Java 버전이었다.

현재 사용 가능한 LTS(Long-Term Support) 버전은 Java 17과 Java 21이다. 새로 시작하는 프로젝트인 만큼 최신 LTS인 Java 21을 선택했고, 그 이유는 다음과 같다.

최신 LTS = 가장 긴 지원 기간

Java 21은 2023년 9월 출시된 최신 LTS 버전이다. LTS 버전은 장기간 지원이 보장되는데, Java 21은 2031년까지 지원된다.

어차피 새로 시작하는 프로젝트라면 굳이 이전 LTS 버전을 선택할 이유는 없다고 생각했다.

Virtual Threads (가상 스레드)

Java 21의 가장 큰 특징은 JEP 444로 정식 도입된 Virtual Threads다.

1
2
3
4
5
// 기존: 플랫폼 스레드 (OS 스레드와 1:1 매핑)
ExecutorService executor = Executors.newFixedThreadPool(200);

// Java 21: 가상 스레드 (경량, 수만 개 생성 가능)
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

Virtual Threads는 기존 플랫폼 스레드보다 훨씬 가벼운 스레드 모델로 많은 수의 동시 요청을 처리해야 하는 서비스에서 유용하게 사용할 수 있다.

현재 Degging 프로젝트에서는 Virtual Threads 사용 여부를 아직 확정하지는 않았지만,

필요한 경우 별도의 리액티브 스택(WebFlux) 없이도 높은 동시성을 확보할 수 있다는 점이 매력적인 기능이라고 생각했다.

Degging은 외부 API(네이버 데이터랩, 카카오맵 등)를 빈번하게 호출하는 서비스다. 기존에는 이런 I/O 바운드 작업에서 블로킹이 발생하면 스레드 풀이 금방 고갈되었지만, Virtual Threads를 사용하면 수만 개의 동시 요청도 가볍게 처리할 수 있다.

그래서 이러한 기능을 활용할 수 있는 환경을 고려해 Java 21을 선택했다.

Pattern Matching & Record 패턴

Java 21에서는 Pattern Matching 기능이 더 확장되었다.

예를 들어 instanceof 패턴 매칭을 사용하면 객체 타입을 확인하면서 바로 변수로 사용할 수 있다.

1
2
3
4
// Record 패턴으로 간결한 분해
if (obj instanceof CafeLocation(double lat, double lng)) {
    // lat, lng를 바로 사용 가능
}

또한 switch에서도 패턴 매칭을 사용할 수 있다.

1
2
3
4
5
6
// Switch 패턴 매칭 (Java 21에서 정식)
String result = switch (errorCode) {
    case CommonErrorCode c -> c.getMessage();
    case CafeErrorCode c -> "카페 관련: " + c.getMessage();
    default -> "알 수 없는 에러";
};

DTO 전달이 많은 백엔드 프로젝트에서 이런 이런 문법이 코드를 훨씬 간결하게 만들어준다.

Sequenced Collections

Java 21에서는 컬렉션 관련 API도 조금 더 편리해졌다.

기존에 List의 마지막 원소를 가져오려면 기존에는 이렇게 작성해야 했다.

1
list.get(list.size() - 1)

하지만 Java 21에서는 다음과 같이 사용할 수 있다.

1
list.getLast()

작은 변화지만, 이런 것들이 쌓이면 코드 가독성이 좋아진다.


Spring Boot 3.5.1을 선택한 이유

Java 21을 정했으니 프레임워크도 자연스럽게 따라갔다.

Spring Boot 3.x는 Java 17 이상을 요구하고, 내부적으로 Spring Framework 6 기반으로 동작한다. 그중에서도 프로젝트 시작 시점에 가장 최신 안정 버전이었던 3.5.1을 채택했다.

항목Spring Boot 2.xSpring Boot 3.x
Java 최소 버전Java 8Java 17
기반 프레임워크Spring 5Spring 6
Jakarta EEjavax.*jakarta.*
Virtual Threads미지원지원
GraalVM Native실험적정식 지원

특히 Spring Boot 3.x에서 Virtual Threads를 설정 한 줄로 활성화할 수 있다.

1
2
3
4
5
# application.yml
spring:
  threads:
    virtual:
      enabled: true

이 설정을 추가하면 내장 톰캣이 요청마다 Virtual Thread를 사용하게 된다. 덕분에 별도의 리액티브 프로그래밍(WebFlux) 없이도 높은 동시성을 확보할 수 있다.


다음 편에서는 프로젝트에 DDD(Domain-Driven Design) 설계를 도입한 이유를 다뤄보겠다.

📌 다음 글: Degging (2) - DDD 설계를 선택한 이유

This post is licensed under CC BY 4.0 by the author.