[Java] 빈 문자열을 split(",")으로 나누었을 때 String[]의 length는 1이다.

손경이·2024년 3월 24일
1

2024.03.22
[테킷 백엔드] 프로젝트 - alcoholfriday
환경 - 스프링부트 3.2.1, 자바 JDK17
작업 - 상품 검색


💡 빈 문자열을 split(",")으로 나누었을 때 String[]의 length는 1이다.

  • 상품 검색에서 카테고리에 빈 값이 들어오면 전체 Item을 검색하는 QueryDSL 코드를 작성했다.
// 카테고리 검색 조건 생성
BooleanExpression categoryPredicate = categories.isEmpty()
        ? item.isNotNull() // 카테고리 조건이 없는 경우 기본적으로 모든 아이템을 선택
        : categories.stream()
        .map(category.lastName::eq) // categoryLastName -> category.lastName.eq(categoryLastName)
        .reduce(BooleanExpression::or)
        .orElse(null);
  • ItemController에서 search() 매개변수로 String categories에 빈 값이 들어오고 그 빈 값을 바로 List.of(categories.split(","))List<String>으로 만들었다.
    (리스트가 필요해서 배열을 리스트로 만들었다.)
  • 당연히 리스트도 비어있다고 생각했다.
    그런데 if(categories.isEmpty())를 하면 내 예상과 다르게 item.isNotNull()에 도달하지 않았다.
  • 처음에는 내가 검색 조건 QueryDSL을 잘못 작성했나 싶어서 검색 조건을 이리저리 바꾸었다.
  • 그러다가 리스트 categories 의 사이즈가 궁금해서 출력문을 입력해서 알아보니 1이 나온다... 왜? 빈 값인데 1이 나오지... 라는 생각과 함께 Claude에 물어보았다.
    • String.split(regex) 메서드의 동작 방식의 경우 정규식과 일치하는 부분을 기준으로 문자열을 분리한다.
    • 빈 문자열("")의 경우에도 길이가 0이 아닌 1인 배열 요소를 생성한다.
    • String categories = "";
       String[] splitResult = categories.split(","); // splitResult = {""}{length=1}
       List<String> parseType = List.of(splitResult); // parseType = [""]{size=1}
       System.out.println(parseType.size()); // 출력: 1

💡 이를 방지하기 위해서 (빈 문자열을 split()으로 나누어도 빈 배열, 빈 리스트가 필요할 때)

  • String categories가 null이거나 빈 문자열("")인 경우를 항상 먼저 확인하자.
    (null일 경우 NullPointerException 발생)
  • 확인 후 빈 리스트를 명시적으로 생성하자!
List<String> parseType = categories == null || categories.isBlank()
                ? Collections.emptyList()
                : List.of(categories.split(","));

- 전체 코드

  • ItemController
@GetMapping
@Operation(summary = "검색어로 전체 상품 조회", description = "검색어와 검색유형에 따라 전체 상품을 여러개 조회한다.")
public ResponseEntity<PageResponse<SearchItemResponse>> search(
        @RequestParam(name = "page", defaultValue = "0") int page,
        @RequestParam(name = "size", defaultValue = "12") int size,
        @RequestParam(name = "keyword") String keyword,
        @RequestParam(name = "categories") @Schema(example = "탁주/막걸리 또는 과실주/와인") String categories
) {
    List<String> parseType = categories == null || categories.isBlank()
            ? Collections.emptyList()
            : List.of(categories.split(","));

    PageResponse<SearchItemResponse> pageResponse = PageResponse.of(this.itemService.search(page, size, keyword, parseType));
    return ResponseEntity.ok().body(pageResponse);
}

  • ItemRepositoryImpl
    • 상품 검색 시 카테고리와 키워드로 검색 가능
@RequiredArgsConstructor
public class ItemRepositoryImpl implements ItemRepositoryCustom {
    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public Page<Item> search(List<String> categories, String keyword, Pageable pageable) {
        // 카테고리 검색 조건 생성
        BooleanExpression categoryPredicate = categories.isEmpty()
                ? item.isNotNull() // 카테고리 조건이 없는 경우 기본적으로 모든 아이템을 선택
                : categories.stream()
                .map(category.lastName::eq) // categoryLastName -> category.lastName.eq(categoryLastName)
                .reduce(BooleanExpression::or)
                .orElse(null);

        // 키워드 검색 조건 생성 - keyword가 없는 경우 카테고리로만 검색
        BooleanExpression searchPredicate = keyword.isBlank()
                ? categoryPredicate
                : categoryPredicate.and(item.name.contains(keyword))
                .or(categoryPredicate.and(product.name.contains(keyword)))
                .or(categoryPredicate.and(maker.name.contains(keyword)));

        List<Item> items = jpaQueryFactory
                .select(item)
                .from(item)
                .leftJoin(itemProduct).on(item.eq(itemProduct.item))
                .leftJoin(product).on(itemProduct.product.eq(product))
                .leftJoin(maker).on(product.maker.eq(maker))
                .leftJoin(category).on(item.category.eq(category))
                .where(searchPredicate)
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        JPAQuery<Long> total = jpaQueryFactory
                .select(item.count())
                .from(item)
                .leftJoin(itemProduct).on(item.eq(itemProduct.item))
                .leftJoin(product).on(itemProduct.product.eq(product))
                .leftJoin(maker).on(product.maker.eq(maker))
                .leftJoin(category).on(item.category.eq(category))
                .where(searchPredicate);

        return PageableExecutionUtils.getPage(items, pageable, total::fetchOne);
    }
}

참고

  • Claude

0개의 댓글