쇼핑몰 만들기 프로젝트 - elastic search를 사용해보자

yeom yaloo·2023년 3월 8일
0


📌보면 좋을 정보: https://tecoble.techcourse.co.kr/post/2021-10-19-elasticsearch/


1. Elastic search란?

일래스틱서치는 루씬 기반의 검색 엔진이다. HTTP 웹 인터페이스와 스키마에서 자유로운 JSON 문서와 함께 분산 멀티테넌트 지원 전문 검색 엔진을 제공한다. 일래스틱서치는 자바로 개발되어 있으며 아파치 라이선스 조항에 의거하여 오픈 소스로 출시되어 있다.
📌 출처: https://ko.wikipedia.org/wiki/%EC%9D%BC%EB%9E%98%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98

  • 자바 기반의 검색 엔진이다.
  • 저장, 검색, 분석이 가능하다.
  • 단독으로 사용하거나 ELK(Elasticsearch / Logstatsh / Kibana) 스택으로 사용하기도 한다.

엘라스틱 서치와 관계형 데이터베이스의 상관관계


📌출처: https://www.slideshare.net/deview/2d1elasticsearch


2. 설정

2-1. docker를 사용한 elastic search 다운

  • 도커를 사용한 엘라스틱서치 다운로드 : docker pull docker.elastic.co/elasticsearch/elasticsearch:8.5.3
  • 실행: docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.5.3

본인의 프로젝트 구성

  • maven (build tool)
  • spring boot (3.0.2 version)
  • jdk 17

호환가능한 버전이 다 다르니 이 부분을 유의하며 진행할 것
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#new-features.5-0-0

2-2. 프로젝트에 의존성 주입(maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
  • spring boot 사용으로 spring-boot-starter-data-elasticsearch 주입

2-3. configuration 설정

@Configuration
public class ElasticConfig extends ElasticsearchConfiguration {

    @Value("${spring.elastic.url}")
    private String elasticUrl;

    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo(elasticUrl)
                .build();
    }
}

2-4. 로그


logging.level.org.springframework.data.elasticsearch.client.WIRE=TRACE

2-5. Reference documents url

https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference


3. 사용

  • 엘라스틱 서치를 사용하면 레포지토리에서 메서드 정의를 통해서 쿼리식 표현도 가능하다.(jpa처럼 JpaRepository를 상속 받으면 정의해놓은 메소드를 메소드명 규칙에 맞게만 작성하면 사용가능한 것과 같이..) ..계속해서 작성

3-0. 엘라스틱서치와 관계형데이터베이스

  • 구성들이 서로 어떤 이름으로 사용되는지 확인하자.

3-1. 인덱스 클래스(Document)

package com.yaloostore.shop.product.documents;


import com.yaloostore.shop.helper.Indices;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;

import java.time.LocalDateTime;

/**
 * 엘라스틱 서치에 사용되는 인덱스 입니다.(관계형 디비 table - 엘라스틱서치 index)
 * */
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
@Setting(settingPath = "/static/elastic-settings.json")
@Document(indexName = Indices.PRODUCTS_INDEX)
public class SearchProduct {

    @Id
    @Field(name = "product_id", type = FieldType.Long)
    private Long productId;

    @Field(name = "productName", type = FieldType.Text)
    private String productName;

    @Field(name = "product_id", type = FieldType.Long)
    private Long stock;

    @Field(name = "product_created_at", type =FieldType.Date)
    private LocalDateTime productCreatedAt;

    @Field(type=FieldType.Text)
    private String description;

    @Field(name = "thumbnail_url",type=FieldType.Text)
    private String thumbnailUrl;

    @Field(name = "fixed_price", type=FieldType.Long)
    private Long fixedPrice;

    @Field(name = "raw_price", type=FieldType.Long)
    private Long rawPrice;

    @Field(name = "is_selled", type=FieldType.Boolean)
    private Boolean isSelled;

    @Field(name = "is_deleted", type=FieldType.Boolean)
    private Boolean isDeleted;

    @Field(name ="is_expose", type=FieldType.Boolean)
    private Boolean isExpose;

    @Field(name = "discount_percent", type=FieldType.Long)
    private Long discountPercent;


}

3-2. 레포지토리

package com.yaloostore.shop.product.repository.elasticSearch;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.yaloostore.shop.product.documents.SearchProduct;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;


/**
 * 엘라스틱 서치를 사용해서 상품 검색을 위한 레포지토리입니다.
 * */


public interface ElasticCommonProductRepository extends ElasticsearchRepository<SearchProduct, Long> {

    Page<SearchProduct> findByProductName(Pageable pageable, String productName);

    Optional<SearchProduct> findById(Long productId);
}
  • JpaRepository 사용법과 비슷하다.
  • ElasticsearchRepository<indexClass, IdType>를 상속받아 진행한다.
  • 추가로 복잡한 검색을 원한다면 쿼리문을 따로 학습 후 진행하면 된다.

3-3. 서비스

package com.yaloostore.shop.product.service.elasticSearch;

import com.yaloostore.shop.common.dto.PaginationResponseDto;
import com.yaloostore.shop.product.dto.response.SearchProductResponseDto;
import com.yaloostore.shop.product.dto.transfer.SearchProductTransfer;
import com.yaloostore.shop.product.documents.SearchProduct;
import com.yaloostore.shop.product.repository.elasticSearch.ElasticCommonProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ElasticProductServiceImpl implements ElasticProductService {

    private final ElasticCommonProductRepository elasticCommonProductRepository;

    /**
     * {@inheritDoc}
     * */
    @Override
    public Page<SearchProductResponseDto> searchProductByProductName(Pageable pageable, String productName) {
        Page<SearchProduct> searchProducts = elasticCommonProductRepository.findByProductName(pageable,productName);

        List<SearchProductResponseDto> response = searchProducts.stream()
                .map(SearchProductTransfer::fromEntity).collect(Collectors.toList());

        //cotent pageable, total
        return new PageImpl<>(response, pageable, searchProducts.getTotalElements());
    }
}

3-4. 컨트롤러

package com.yaloostore.shop.product.controller.elasticSearch;


import com.yalooStore.common_utils.dto.ResponseDto;
import com.yaloostore.shop.common.dto.PaginationResponseDto;
import com.yaloostore.shop.product.dto.response.SearchProductResponseDto;
import com.yaloostore.shop.product.service.elasticSearch.ElasticProductService;
import jakarta.validation.constraints.Size;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


/**
 * 엘라스틱 서치를 사용해서 상품 검색을 하는 컨트롤러입니다.
 * */
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/service/products/search")
public class ElasticProductRestController {

    private final ElasticProductService elasticProductService;


    @GetMapping(params = "productName")
    public ResponseEntity<ResponseDto> searchProductByProductName(@RequestParam @Size(max = 30) String productName,
                                                                                                     @PageableDefault Pageable pageable){

        Page<SearchProductResponseDto> response = elasticProductService.searchProductByProductName(pageable, productName);


        return ResponseEntity.ok(
                ResponseDto.builder()
                        .success(true)
                        .status(HttpStatus.OK)
                        .data(response.getContent())
                        .build()

        );

    }                                                            
}
  • 기본 사용은 완료되었다.
  • 여기서 더 복잡한 쿼리를 사용하고 싶다면 elasticsearch query dsl을 사용하도록하자

4. 에러 발생?!과 해결

profile
즐겁고 괴로운 개발😎

0개의 댓글