[Co Labor] 검색 기능 구현
2024. 9. 4. 21:40ㆍ프로젝트: Co Laobr
요구사항 정리
- 검색하려는 키워드를 채용 공고, 기업 리뷰, 기업 정보 중에서 검색
- 채용 공고 중 title, description 엔티티에서 검색
- 기업 리뷰 중 pros, cons 엔티티에서 검색
- 기업 정보 중 name, address, description 엔티티에서 검색
Repository
EnterpriseRepository
package pelican.co_labor.repository.enterprise;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import pelican.co_labor.domain.enterprise.Enterprise;
import java.util.List;
@Repository
public interface EnterpriseRepository extends JpaRepository<Enterprise, Long> {
@Query("SELECT e FROM Enterprise e WHERE LOWER(e.name) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(e.address) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(e.description) LIKE LOWER(CONCAT('%', :keyword, '%'))")
List<Enterprise> searchEnterprises(@Param("keyword") String keyword);
}
- @Query("SELECT e FROM Enterprise e WHERE e.name LIKE %:keyword% OR e.address LIKE %:keyword% OR e.description LIKE %:keyword%") List<Enterprise> searchEnterprises(@Param("keyword") String keyword);
: 특정 키워드를 사용하여 Enterprise 엔티티를 검색하는 커스텀 쿼리를 정의한다. @Param 으로 메서드의 파라미터와 쿼리의 %:keyword% 를 매핑하는 역할을 한다.
ReviewRepository
package pelican.co_labor.repository.review;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import pelican.co_labor.domain.review.Review;
import java.util.List;
@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
@Query("SELECT r FROM Review r WHERE LOWER(r.pros) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(r.cons) LIKE LOWER(CONCAT('%', :keyword, '%'))")
List<Review> searchReviews(@Param("keyword") String keyword);
}
JobRepository
package pelican.co_labor.repository.job;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import pelican.co_labor.domain.job.Job;
import java.util.List;
@Repository
public interface JobRepository extends JpaRepository<Job, Long> {
@Query("SELECT j FROM Job j WHERE LOWER(j.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR LOWER(j.description) LIKE LOWER(CONCAT('%', :keyword, '%'))")
List<Job> searchJobs(@Param("keyword") String keyword);
}
Service
SearchService
package pelican.co_labor.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pelican.co_labor.domain.enterprise.Enterprise;
import pelican.co_labor.domain.job.Job;
import pelican.co_labor.domain.review.Review;
import pelican.co_labor.repository.enterprise.EnterpriseRepository;
import pelican.co_labor.repository.job.JobRepository;
import pelican.co_labor.repository.review.ReviewRepository;
import java.util.List;
@Service
public class SearchService {
@Autowired
private JobRepository jobRepository;
@Autowired
private ReviewRepository reviewRepository;
@Autowired
private EnterpriseRepository enterpriseRepository;
public List<Job> searchJobs(String keyword) {
return jobRepository.searchJobs(keyword);
}
public List<Review> searchReviews(String keyword) {
return reviewRepository.searchReviews(keyword);
}
public List<Enterprise> searchEnterprises(String keyword) {
return enterpriseRepository.searchEnterprises(keyword);
}
}
- 비즈니스 로직을 독립적으로 테스트하기 위해 서비스 계층으로 분리하였다.
Controller
SearchController
package pelican.co_labor.controller;
import org.springframework.beans.factory.annotation.Autowired;
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;
import pelican.co_labor.domain.enterprise.Enterprise;
import pelican.co_labor.domain.job.Job;
import pelican.co_labor.domain.review.Review;
import pelican.co_labor.service.SearchService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/search")
public class SearchController {
@Autowired
private SearchService searchService;
@GetMapping
public Map<String, Object> search(@RequestParam String keyword) {
Map<String, Object> response = new HashMap<>();
List<Job> jobs = searchService.searchJobs(keyword);
List<Review> reviews = searchService.searchReviews(keyword);
List<Enterprise> enterprises = searchService.searchEnterprises(keyword);
response.put("jobs", jobs);
response.put("reviews", reviews);
response.put("enterprises", enterprises);
return response;
}
}
- http://localhost:8080/search?keyword=tech 와 같은 링크로 요청하면 된다.
이제 Postman으로 테스트하면 되는데 일단 더미데이터를 기반으로 테스트해봤다.
각 엔티티에서 OneToMany, ManyToOne 모두 관계를 설정했더니 연관된 관계를 모두 JSON으로 리턴하면서 review와 labor_user가 서로를 호출하는 무한루프가 발생했고 OneToMany 관계를 모두 삭제했다. 그 후 결과는 다음과 같다.
{
"reviews": [
{
"review_id": 1,
"laborUser": {
"labor_user_id": 1,
"password": "password123",
"name": "Alice Johnson",
"email": "alice.johnson@example.com",
"created_at": "2024-07-09T18:29:26.624766"
},
"enterprise": {
"enterprise_id": 1,
"name": "Tech Company",
"address": "123 Tech Street",
"description": "Leading tech company in the industry.",
"created_at": "2024-07-09T18:29:26.568681"
},
"title": "Tech Place to Work",
"rating": 5,
"promotion_rating": 4,
"salary_rating": 3,
"balance_rating": 5,
"culture_rating": 4,
"management_rating": 4,
"pros": "Great work-life balance and culture.",
"cons": "Salary could be higher.",
"like_count": 10,
"created_at": "2024-07-09T18:29:26.661496",
"modified_at": "2024-07-09T18:29:26.661496"
}
],
"jobs": [
{
"job_id": 1,
"enterpriseUser": {
"enterprise_user_id": 1,
"password": "password123",
"name": "John Doe",
"email": "john.doe@techcompany.com",
"created_at": "2024-07-09T18:29:26.614783",
"enterprise": {
"enterprise_id": 1,
"name": "Tech Company",
"address": "123 Tech Street",
"description": "Leading tech company in the industry.",
"created_at": "2024-07-09T18:29:26.568681"
}
},
"enterprise": {
"enterprise_id": 1,
"name": "Tech Company",
"address": "123 Tech Street",
"description": "Leading tech company in the industry.",
"created_at": "2024-07-09T18:29:26.568681"
},
"title": "Tech Engineer",
"description": "Develop and maintain software solutions.",
"gender": "Any",
"age": "Any",
"views": 100,
"dead_date": "2024-08-08T18:29:26.639517",
"created_at": "2024-07-09T18:29:26.645533",
"modified_at": "2024-07-09T18:29:26.645533"
}
],
"enterprises": [
{
"enterprise_id": 1,
"name": "Tech Company",
"address": "123 Tech Street",
"description": "Leading tech company in the industry.",
"created_at": "2024-07-09T18:29:26.568681"
}
]
}
- review : laborUser, enterprise 정상 출력
- job : enterpriseUser, enterprsie 정상 출력
- enterprise : 연관된 엔티티 출력하지 않음.
- 원래 enterprise와 연관된 job, review를 출력할까 생각했지만 해당 컨트롤러는 검색이므로 기업 정보만 출력하고 기업 상세 컨트롤러에서 job, review 등을 출력하는 편이 더 좋다고 판단하여 그대로 두기로 결정하였다.
'프로젝트: Co Laobr' 카테고리의 다른 글
[Co Labor] Spring local to React local 연동, CORS 설정 (0) | 2024.10.25 |
---|---|
[Co Labor] 스프링과 MySQL에 기업 데이터 추가하기 공개 데이터 포털 API 사용! (0) | 2024.10.14 |
[Co Labor] 스프링 - 엔티티, 도메인 정의 및 컨트롤러 기초 (0) | 2024.09.02 |
[Co Labor] 패키지, 데이터베이스 설계 및 스프링부트 프로젝트 생성, Hibernate, 스프링부트 프로젝트 초기 오류 및 MySQL 오류 잡기 (0) | 2024.08.08 |
[Co Laobr] 개발 동기와 관련 데이터 찾기, 스토리보드 작성 (0) | 2024.08.07 |