SpringBoot에서 Elasticsearch 이용하기

..·2024년 6월 19일
0

Elasticsearch

목록 보기
4/7

🛠️ dependency 추가하기

implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")

Elasticsearch를 이용하기 위해서 SpringBoot 프로젝트의 build.gradle.kts에 dependency를 추가했다.

ElasticsearchConfig 작성하기

Spring Data Elasticsearch는 단일 Elasticsearch 노드 또는 클러스터에 연결된 Elasticsearch 클라이언트에서 작동한다.
클라이언트를 작동하게 하기 위해서는 아래의 Configuration을 필수적으로 작성하여 사용해야 한다.

@Configuration
public class ElasticsearchConfig extends ElasticsearchConfiguration {
    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
    }
}

Document Entity 작성하기

Spring Data Elasticsearch 객체 매핑은 Java 객체를 Elasticsearch에 저장된 JSON 표현으로 매핑하는 프로세스이다.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
@Builder
@Document(indexName = "item")
public class Item {
    @Id
    @org.springframework.data.annotation.Id
    @Field(name = "id", type = FieldType.Keyword)
    private String itemId;

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

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

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

    @Field(type = FieldType.Integer)
    private int price;
}

Document

Elasticsearch에서 실제로 데이터가 저장되는 단위를 나타낸다.
작성한 클래스가 데이터베이스에 매핑되어야 한다는 것을 나타내기 위해 클래스 단에서 @Document 어노테이션을 적용한다.
작성한 엔티티는 item이라는 이름의 인덱스에 매핑된다.

Index

Document를 저장하는 논리적 단위로 RDB의 테이블과 비슷하다고 생각했다.
RDB에서는 하나의 테이블에서 여러 개의 데이터가 모두 같은 컬럼을 가지게 되는데, 하나의 Index에 포함된 Document는 서로 다른 field를 가질 수 있다고 한다.


Repository 작성하기

public interface ItemSearchRepository extends ElasticsearchRepository<Item, Long> {
    List<Item> findByName(String keyword);
}

Elasticsearch를 사용하기 위해서는 ElasticsearchRepository를 상속받는 인터페이스를 구현한다.
나는 document의 name으로 검색하는 기능을 구현하고 싶어서 findByName을 선언했다.

Service 작성하기

@Service
@RequiredArgsConstructor
public class ItemSearchService {

    private final ItemSearchRepository itemSearchRepository;
    
    public Item createItem(Item item) {
        return itemSearchRepository.save(item);
    }

    public List<Item> getItemByName(String keyword) {
        List<Item> list = itemSearchRepository.findByName(keyword);
        return list;
    }
}

item document를 생성하는 메소드와 name으로 검색하는 메소드를 작성했다.

Controller 작성하기

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/item/search")
public class ItemSearchController {

    private final ItemSearchService itemSearchService;

    @GetMapping
    public DataResponseDTO<List<Item>> search(@RequestParam("keyword") String keyword) {
        return DataResponseDTO.of(itemSearchService.getItemByName(keyword));
    }

    @PostMapping
    public DataResponseDTO<Item> create(@RequestBody Item item) {
        return DataResponseDTO.of(itemSearchService.createItem(item));
    }
}

/api/item/search를 이용하여 RequestBody를 Item document의 형식으로 POST할 경우에는 새로운 document를 생성하고, RequestParam을 keyword로 보내면 검색에 성공한 document 목록을 반환하는 컨트롤러를 작성했다.

🚨 No id property found for class

Elasticsearch를 실행하고, SpringBoot 프로젝트를 실행하니 이런 오류가 발생했다. Item 엔티티에서 @Id를 인식하지 못하는 것 같았다.

@org.springframework.data.annotation.Id

엔티티의 Id를 인식하게 하기 위해 추가적으로 작성했더니 이후에는 Id를 인식했다.

🚨 Elasticsearch Bean 오류

unsatisfied dependency expressed through constructor parameter 0

@enableelasticsearchrepositories declared on elasticsearchrepositoriesregistrar.enableelasticsearchrepositoriesconfiguration

@Id 인식 오류를 해결하니 또 다른 오류가 발생했다. Elasticsearch를 사용하면서 발생하는 오류임은 확실했는데, 대체 어떤 이유로 오류가 나는지 이해할 수가 없었다.

중복된 Bean의 등록

JpaRepositoriesRegistrarElasticsearchRepositories는 동일하게 ReposistoryBeanDefinitionRegistrarSupport를 상속받는다. ReposistoryBeanDefinitionRegistrarSupport는 프로그램이 실행되면서 Bean이 등록되는데, 이로 인해 중복된 Bean이 등록되어 오류가 발생한 것이었다.

@EnableJpaRepositories(excludeFilters = @ComponentScan.Filter(
		type = FilterType.ASSIGNABLE_TYPE,
		classes = ItemSearchRepository.class))
@SpringBootApplication
public class MycafeApplication {

	public static void main(String[] args) {
		SpringApplication.run(MycafeApplication.class, args);
	}

}

따라서 @EnableJpaRepositories를 이용하여 ElasticsearchRepository인 ItemSearchRepository가 자동으로 Bean 등록이 되지 않게 Filter 코드를 작성했다.

🚨 등록은 되는 것 같은데 검색이 안돼...!

위에 작성한 내용을 바탕으로 새로운 Document를 생성했다.
근데 GET으로 검색을 요청하면 어디선가 문제가 발생하여 빈 배열만 반환됐다.
왜지

Kibana에서 확인한 Index



작성한 Document Entity인 item이라는 이름으로 새로운 인덱스가 생성된 것을 확인했다.



마무리

아직 kibana를 어떤 방식으로 사용하는지 잘 모르기도 하고 대시보드를 봐도 이해할 수 없어서 데이터 저장은 된 것 같은데 어떤 방식으로 저장되었고 사용할 수 있을지 더 공부해야겠다. 일단 SpringBoot에서 Elasticsearch를 연동하여 document를 생성하는 과정에서 SpringBoot에서 사용하는 각종 어노테이션이나 이를 통한 Bean 등록에 대해 다시 한번 공부하게 되었다.
문서를 처음부터 다 읽고 프로젝트를 만들 수 없으니 이렇게 직접 부딪히고 깨져가며 만드는데 이런 식으로 체득하니까 힘들긴 해도 확실히 이해할 수 있는 것 같다.
다음에는 kibana의 DevTool을 이용하여 Elasticsearch의 기능을 이용하는 과정에 대해 먼저 이해를 하고, 이를 바탕으로 SpringBoot에서 API 형식으로 구현해 봐야겠다.

참고

Elasticsearch 클라이언트
Elasticsearch + SpringBoot로 검색 기능 구현
JPA Bean
Elasticsearch Bean Error

0개의 댓글