2026년 02월 01일

🌵Spring Data JPA, 쿼리 메서드와 추천 상품 로직

Java Spring Boot
Cover Image

🌵 Spring Data JPA로 다육식물 쇼핑몰 만들기

📚 목차

  1. findByCategoryIn - 마법같은 쿼리 메서드
  2. getHomeFeaturedProducts - 홈 화면 추천 상품 로직
  3. 핵심 요약

1. findByCategoryIn - 마법같은 쿼리 메서드 🪄

📝 코드부터 보기

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByCategoryIn(Collection<String> categories);
}

🤔 이게 어떻게 동작하는 거죠?

findByCategoryIn(Collection<String> categories)는 Spring Data JPA의 쿼리 메서드(Query Method) 기능입니다. 개발자가 SQL을 직접 작성하지 않아도, 메서드 이름만으로 복잡한 쿼리를 실행할 수 있는 강력한 기능이죠!

⚙️ 동작 원리

1️⃣ SQL IN 연산자 자동 생성

JPA는 메서드 이름의 In 키워드를 분석하여 다음과 같은 SQL을 자동으로 생성합니다:

SELECT * FROM product WHERE category IN (?, ?, ?)

2️⃣ 다중 조건 검색의 편리함

인자로 전달된 Collection<String>(List, Set 등)에 포함된 여러 카테고리 중 하나라도 일치하는 상품들을 모두 찾아옵니다.

💡 실제 사용 예시:

List<String> leafCategories = List.of("에케베리아", "세덤", "하월시아");
List<Product> products = productRepository.findByCategoryIn(leafCategories);
// → "에케베리아", "세덤", "하월시아" 카테고리의 모든 상품을 조회!

3️⃣ 추상화된 데이터 접근

개발자가 직접 SQL을 작성하지 않아도, 인터페이스 정의만으로 복잡한 다중 조건 조회를 안전하게 처리할 수 있습니다.

✅ 장점 정리

장점설명
🎯 타입 안정성컴파일 타임에 오류 발견 가능
🧹 깔끔한 코드SQL 작성 없이 메서드 이름만으로 구현
🔒 SQL 인젝션 방지PreparedStatement 자동 사용
📖 가독성메서드 이름이 곧 문서

2. getHomeFeaturedProducts - 홈 화면 추천 상품 로직 🏠

📝 전체 코드

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ProductService {

    private static final int HOME_FEATURED_LIMIT = 3;

    /** 잎다육 카테고리 (/shop) */
    private static final List<String> LEAF_CATEGORIES = List.of(
            "에케베리아", "세덤", "하월시아", "크라슐라", "칼랑코에", "기타 잎다육"
    );

    /** 홈 잎다육 탭 (HOME_FEATURED + SUCCULENT_PLANTS) */
    private static final List<String> LEAF_CATEGORIES_FOR_HOME = List.of(
            "잎다육", "에케베리아", "세덤", "하월시아", "크라슐라", "칼랑코에", "기타 잎다육"
    );

    /** 줄기 다육 카테고리 (/shop/stem) */
    private static final List<String> STEM_CATEGORIES = List.of(
            "선인장", "유포르비아", "파키포디움", "아데니움", "기타 줄기다육"
    );

    /** 코덱스 다육 카테고리 (/shop/caudex) */
    private static final List<String> CAUDEX_CATEGORIES = List.of(
            "아데니움", "파키포디움", "디오스코레아", "포케아", "아데니아", "희귀 코덱스"
    );

    private final ProductRepository productRepository;

    /**
     * 홈 BEST SELLER (/) - 잎다육/줄기 다육/코덱스 다육 탭별 상위 3개
     */
    public List<Product> getHomeFeaturedProducts(String tab) {
        List<Product> products;
        if ("잎다육".equals(tab)) {
            products = productRepository.findByCategoryIn(LEAF_CATEGORIES_FOR_HOME);
        } else if ("줄기 다육".equals(tab)) {
            products = productRepository.findByCategoryIn(STEM_CATEGORIES);
        } else if ("코덱스 다육".equals(tab)) {
            products = productRepository.findByCategoryIn(CAUDEX_CATEGORIES);
        } else {
            products = productRepository.findByCategory(tab);
        }
        return products.stream()
                .limit(HOME_FEATURED_LIMIT)
                .toList();
    }
}

🎯 메서드의 목적

이 메서드는 메인 화면(Home)의 "BEST SELLER" 섹션에서 탭을 전환할 때, 각 카테고리에 맞는 추천 상품을 제공합니다.

🔄 동작 흐름 분석

Step 1️⃣ 탭에 따른 카테고리 분류

사용자가 선택한 tab 파라미터에 따라 조회할 카테고리 목록을 결정합니다.

탭 선택: "잎다육" 클릭
    ↓
LEAF_CATEGORIES_FOR_HOME 사용
    ↓
["잎다육", "에케베리아", "세덤", "하월시아", "크라슐라", "칼랑코에", "기타 잎다육"]

🔹 다중 카테고리 처리

🔹 개별 카테고리 처리

Step 2️⃣ 메모리 내 데이터 제한

return products.stream()
        .limit(HOME_FEATURED_LIMIT)  // 최대 3개
        .toList();

Repository에서 가져온 전체 상품 리스트를 Java Stream API로 가공합니다.

🎨 UI/UX 관점에서 보기

┌─────────────────────────────────────┐
│         BEST SELLER 🏆              │
│                                      │
│  [잎다육] [줄기 다육] [코덱스 다육] │ ← 탭 메뉴
│  ─────                               │
│                                      │
│  🌱 에케베리아 '라울'     15,000원  │
│  🌱 세덤 '오로라'         8,000원   │
│  🌱 하월시아 '옵투사'     12,000원  │
│                                      │
└─────────────────────────────────────┘

💡 설계 포인트

포인트설명
📌 상수 활용카테고리 그룹을 상수로 관리하여 유지보수성 향상
🎯 명확한 책임탭 → 카테고리 → 상품 조회의 단계가 명확함
🔄 유연성새로운 탭 추가 시 else if 분기만 추가하면 됨
⚡ 성능 고려limit()으로 불필요한 데이터 전송 방지

🚀 개선 포인트

현재 코드는 메모리 상에서 limit을 적용하지만, 더 효율적으로는:

// 개선안: DB 쿼리 레벨에서 제한
List<Product> findTop3ByCategoryIn(Collection<String> categories);

이렇게 하면 DB에서 3개만 가져오므로 네트워크 비용과 메모리 사용량이 줄어듭니다!


🎓 핵심 요약

findByCategoryIn

getHomeFeaturedProducts


← 목록으로 돌아가기