3-13. 검색 기능 추가하기

검색 기능 구현하기

검색 기능을 구현하려면 다음과 가튼 SQL 쿼리가 실행되어야한다.

select
	distinct q.id,
	q.author_id,
	q.content,
	q.create_date,
	q.modify_date,
	q.subject
from question q
left outer join site_user u1 on q.author_id=u1.id
left outer join answer a on q.id=a.question_id
left outer join site_user u2 on a.author_id=u2.id
where
	q.subject like '%스프링%'
	or q.content like '%스프링%'
	or u1.username like '%스프링%'
	or a.content like '%스프링%'
	or u2.username like '%스프링%'

JPA의 Specification 인터페이스 사용하기

QuestionService.java에 해당 코드를 추가한다.

//QuestionService.java
import com.mysite.sbb.answer.Answer;
import org.springframework.data.jpa.domain.Specification;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
...
public class QuestionService {
	private Specification<Question> search(String kw) {
		return new Specification<>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Predicate toPredicate(Root<Question> q, CriteriaQuery<?> query, CriteriaBuilder cb) {
				query.distinct(true); // 중복을 제거
				Join<Question, SiteUser> u1 = q.join("author", JoinType.LEFT);
				Join<Question, Answer> a = q.join("answerList", JoinType.LEFT);
				Join<Answer, SiteUser> u2 = a.join("author", JoinType.LEFT);
				return cb.or(cb.like(q.get("subject"), "%" + kw + "%"), // 제목
						cb.like(q.get("content"), "%" + kw + "%"), // 내용
						cb.like(u1.get("username"), "%" + kw + "%"), // 질문 작성자
						cb.like(a.get("content"), "%" + kw + "%"), // 답변 내용
						cb.like(u2.get("username"), "%" + kw + "%")); // 답변 작성자
			}
		};
	}

질문 리포지터리, 서비스 수정하기

Specification을 통해 질문을 조회하기 위해 QuestionRepository를 수정해야 한다.

//QuestionRepository.java
Page<Question> findAll(Specification<Question> spec, Pageable pageable);
//QuestionService.java 수정
	public Page<Question> getList(int page, String kw) {
        List<Sort.Order> sorts = new ArrayList<>();
        sorts.add(Sort.Order.desc("createDate"));
        Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));
        Specification <Question> spec = search(kw);
        return this.questionRepository.findAll(spec, pageable);
    }

질문 컨트롤러 수정하기

QuestionService의 getList 메서드의 입력 항목이 변경되었기 때문에 값을 받은 컨트롤러도 수정되어야 한다.

//QuestionController.java
	public String list(Model model, @RequestParam(value = "page", defaultValue = "0") int page,
			@RequestParam(value = "kw", defaultValue = "") String kw) {
		Page<Question> paging = this.questionService.getList(page, kw);
		model.addAttribute("paging", paging);
		model.addAttribute("kw", kw);
		return "question_list";
	}

검색 화면 구현하기