Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- Kernighan의 C언어 프로그래밍
- iterator
- 자료구조와함께배우는알고리즘입문
- 선형대수
- ㅒ
- resttemplate
- baeldung
- 코드로배우는스프링부트웹프로젝트
- 티스토리 쿠키 삭제
- 스프링 시큐리티
- 이터레이터
- 네트워크 설정
- network configuration
- 자료구조와 함께 배우는 알고리즘 입문
- 구멍가게코딩단
- 알파회계
- 데비안
- d
- /etc/network/interfaces
- 코드로배우는스프링웹프로젝트
- 목록처리
- 친절한SQL튜닝
- GIT
- 서버설정
- 리눅스
- 자바편
- 스프링부트핵심가이드
- 페이징
- 처음 만나는 AI수학 with Python
- 처음 만나는 AI 수학 with Python
Archives
- Today
- Total
bright jazz music
guestbook : 11. 검색처리(1) 본문
● 검색 처리는 크게 두 가지로 나눌 수 있다.
- 서버 사이드 처리
- 화면 사이드 처리
검색 항목은 대략 아래와 같이 정의한다.
- '제목(t), 내용(c), 작성자(w)'로 검색하는 경우
- '제목(t) 혹은 내용(c)'으로 검색하는 경우
- '제목(t) 혹은 내용(c) 혹은 작성자(w)'로 검색하는 경우
1. 서버 사이드 처리
- PageRequestDTO에 검색타입(type)과 키워드(keyword) 추가
- 이하 서비스 계층에서 Querydsl을 이용해서 처리
1-1. PageRequestDTO에 검색조건(type)과 검색 키워드(keyword) 추가
//PageRequestDTO.java
package com.example.guestbook.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; //페이지 관련 임포트
import org.springframework.data.domain.Sort;
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO {
private int page;
private int size;
//검색을 위해 추가
private String type;
private String keyword;
public PageRequestDTO(){
this.page = 1;
this.size = 10;
}
// 이 DTO의 목적은 JPA에서 사용하는 Pageable 타입의 객체를 생성하는 것이다.
// JPA를 이용하는 경우에는 페이지 번호가 0부터 시작한다.
// 따라서 1페이지의 경우 0이 될 수 있도록 아래와 같이 작성한다.
// 정렬(Sort)는 다양한 상황에서 사용하기 위해서 별도의 파라미터로 받도록 설계.
public Pageable getPageable(Sort sort) {
return PageRequest.of(page - 1, size, sort );
}
}
type과 keyword 속성이 클래스에 추가되었다.
1-2. 서비스 계층의 검색 구현과 테스트
동적으로 검색 조건이 처리되는 경우의 실제 코딩은 아래와 같다.
- Querydsl을 통해 BooleanBuilder 작성
- GuestbookRepository는 Querydsl로 작성된 BooleanBuilder를 findAll()을 처리하는 용도로 사용
BooleanBuilder 작성은 별도의 클래스 등을 작성해서 처리할 수 있다.
그러나 간단히 하려면 GuestbookServiceImple 내에 메서드를 하나 작성해서 처리해도 된다.
//GuestbookImpl.java
package com.example.guestbook.service;
import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import com.example.guestbook.entity.QGuestbook;
import com.example.guestbook.repository.GuestbookRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.function.Function;
@Service //스프링이 빈으로 처리하도록 @Service 어노테이션 추가
@Log4j2
@RequiredArgsConstructor // <== 의존성 자동 주입!
public class GuestbookServiceImpl implements GuestbookService {
private final GuestbookRepository repository; //jpa 처리를 위해 repository 주입! 반드시 fianl 사용
@Override //GuestbookService에서 상속한 메서드 오버라이딩
public Long register(GuestbookDTO dto) {
log.info("DTO--------------------------------------");
log.info(dto);
Guestbook entity = dtoToEntity(dto); // GuestbookService에서 default를 이용하여 생성한 메서드
log.info(entity);
repository.save(entity); // 처리 저장.
// return null;
return entity.getGno(); //처리 후에는 엔티티의 gno 리턴
}
@Override
public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestedDTO){
Pageable pageable = requestedDTO.getPageable(Sort.by("gno").descending());
Page<Guestbook> result = repository.findAll(pageable);
Function<Guestbook, GuestbookDTO> fn = (entity -> entityToDto(entity));
return new PageResultDTO<>(result, fn);
}
//게시글 조회 메서드드
@Override
public GuestbookDTO read(Long gno) {
Optional<Guestbook> result = repository.findById(gno);
return result.isPresent() ? entityToDto(result.get()) : null;
//삼항연사자. GuestbookRepository에서 findById()를 통해 엔티티 객체를 가져온다면
// 이를 DTO로 바꾸어서 반환한다.
}
//게시물 삭제 메서드
@Override
public void remove(Long gno){
repository.deleteById(gno);
}
//게시물 수정 메서드
@Override
public void modify(GuestbookDTO dto){
//업데이트 항목은 '제목', '내용'
//Optional<T> //java.util
Optional<Guestbook> result = repository.findById(dto.getGno());
if(result.isPresent()){
Guestbook entity = result.get();
entity.changeTitle(dto.getTitle());
entity.changeContent(dto.getContent());
repository.save(entity);
}
}
//검색 처리를 위한 BooleanBuilder 생성
private BooleanBuilder getSearch(PageRequestDTO requestDTO) { //Querydsl 처리
String type = requestDTO.getType();
//com.querydsl.core
BooleanBuilder booleanBuilder = new BooleanBuilder();
QGuestbook qGuestbook = QGuestbook.guestbook;
String keyword = requestDTO.getKeyword();
//com.querydsl.core.types.dsl
BooleanExpression expression = qGuestbook.gno.gt(0L); //gno > 0 조건에만 생성
booleanBuilder.and(expression);
if(type == null || type.trim().length() == 0){ //검색 조건이 없는 경우
return booleanBuilder;
}
//검색 조건 작성하기
BooleanBuilder conditionBuilder = new BooleanBuilder();
if(type.contains("t")){
conditionBuilder.or(qGuestbook.title.contains(keyword));
}
if (type.contains("c")){
conditionBuilder.or(qGuestbook.content.contains(keyword));
}
if(type.contains("w")){
conditionBuilder.or(qGuestbook.writer.contains(keyword));
}
//모든 조건 통합
booleanBuilder.and(conditionBuilder);
return booleanBuilder;
}
}
- GuestbookServiceImpl에 작성한 getSearch()는 pageRequestDTO를 파라미터로 받는다.
- 검색 조건(type)이 있는 경우 conditionBuilder 변수를 생성해서 각 검색 조건을 'or'로 연결해서 처리한다.
- 검색 조건(type)이 없는 경우 'gno > 0'으로만 생성된다.
1-3 getList()메서드 수정
검색 결과를 목록화 해서 나타내기 위해 getList() 메서드를 수정한다.
//GuestbookImpl.java
package com.example.guestbook.service;
import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import com.example.guestbook.entity.QGuestbook;
import com.example.guestbook.repository.GuestbookRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.function.Function;
@Service //스프링이 빈으로 처리하도록 @Service 어노테이션 추가
@Log4j2
@RequiredArgsConstructor // <== 의존성 자동 주입!
public class GuestbookServiceImpl implements GuestbookService {
private final GuestbookRepository repository; //jpa 처리를 위해 repository 주입! 반드시 fianl 사용
@Override //GuestbookService에서 상속한 메서드 오버라이딩
public Long register(GuestbookDTO dto) {
log.info("DTO--------------------------------------");
log.info(dto);
Guestbook entity = dtoToEntity(dto); // GuestbookService에서 default를 이용하여 생성한 메서드
log.info(entity);
repository.save(entity); // 처리 저장.
// return null;
return entity.getGno(); //처리 후에는 엔티티의 gno 리턴
}
@Override
public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestedDTO){
Pageable pageable = requestedDTO.getPageable(Sort.by("gno").descending());
//수정
BooleanBuilder booleanBuilder = getSearch(requestedDTO); //검색조건 처리
Page<Guestbook> result = repository.findAll(booleanBuilder, pageable); //Querydsl 사용
Function<Guestbook, GuestbookDTO> fn = (entity -> entityToDto(entity));
return new PageResultDTO<>(result, fn);
}
//게시글 조회 메서드드
@Override
public GuestbookDTO read(Long gno) {
Optional<Guestbook> result = repository.findById(gno);
return result.isPresent() ? entityToDto(result.get()) : null;
//삼항연사자. GuestbookRepository에서 findById()를 통해 엔티티 객체를 가져온다면
// 이를 DTO로 바꾸어서 반환한다.
}
//게시물 삭제 메서드
@Override
public void remove(Long gno){
repository.deleteById(gno);
}
//게시물 수정 메서드
@Override
public void modify(GuestbookDTO dto){
//업데이트 항목은 '제목', '내용'
//Optional<T> //java.util
Optional<Guestbook> result = repository.findById(dto.getGno());
if(result.isPresent()){
Guestbook entity = result.get();
entity.changeTitle(dto.getTitle());
entity.changeContent(dto.getContent());
repository.save(entity);
}
}
//검색 처리를 위한 BooleanBuilder 생성
private BooleanBuilder getSearch(PageRequestDTO requestDTO) { //Querydsl 처리
String type = requestDTO.getType();
//com.querydsl.core
BooleanBuilder booleanBuilder = new BooleanBuilder();
QGuestbook qGuestbook = QGuestbook.guestbook;
String keyword = requestDTO.getKeyword();
//com.querydsl.core.types.dsl
BooleanExpression expression = qGuestbook.gno.gt(0L); //gno > 0 조건에만 생성
booleanBuilder.and(expression);
if(type == null || type.trim().length() == 0){ //검색 조건이 없는 경우
return booleanBuilder;
}
//검색 조건 작성하기
BooleanBuilder conditionBuilder = new BooleanBuilder();
if(type.contains("t")){
conditionBuilder.or(qGuestbook.title.contains(keyword));
}
if (type.contains("c")){
conditionBuilder.or(qGuestbook.content.contains(keyword));
}
if(type.contains("w")){
conditionBuilder.or(qGuestbook.writer.contains(keyword));
}
//모든 조건 통합
booleanBuilder.and(conditionBuilder);
return booleanBuilder;
}
}
1-4. 테스트 코드로 확인(getSearch의 정상작동 여부)
//GuestbookServiceTests.java
package com.example.guestbook.service;
import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.sql.SQLOutput;
@SpringBootTest
public class GuestbookServiceTests {
@Autowired
private GuestbookService service;
@Test
public void testRegister(){
//테스트 객체 생성
GuestbookDTO guestbookDTO = GuestbookDTO.builder()
.title("Sample title...")
.content("Sample Content...")
.writer("user0")
.build();
//service.register() 테스트: 테스트 객체 사용
System.out.println(service.register(guestbookDTO));
}
@Test
public void testList(){ //목록처리 테스트
PageRequestDTO pageRequestDTO = PageRequestDTO.builder()
.page(1)
.size(10)
.build();
PageResultDTO<GuestbookDTO, Guestbook> resultDTO = service.getList(pageRequestDTO);
//테스트에 값 확인 추가
System.out.println("PREV " + resultDTO.isPrev());
System.out.println("NEXT " + resultDTO.isNext());
System.out.println("TOTAL: " + resultDTO.getTotalPage());
for(GuestbookDTO guestbookDTO : resultDTO.getDtoList()) {
System.out.println(guestbookDTO);
}
System.out.println("====================================");
resultDTO.getPageList().forEach(i -> System.out.println(i));
}
@Test //검색 테스트 (getList -> getSearch 테스트)
public void testSearch(){
PageRequestDTO pageRequestDTO = PageRequestDTO.builder()
.page(1)
.size(10)
.type("tc") //검색조건 t, c, w, tc, tcw..
.keyword("한글") //검색 키워드드
.build();
PageResultDTO<GuestbookDTO, Guestbook> resultDTO = service.getList(pageRequestDTO);
System.out.println("PREV" + resultDTO.isPrev());
System.out.println("NEXT" + resultDTO.isNext());
System.out.println("TOTAL" + resultDTO.getTotalPage());
System.out.println("----------------------------------------------");
for (GuestbookDTO guestbookDTO : resultDTO.getDtoList()){
System.out.println("guestbookDTO");
}
}
}
위의 테스트 코드는 제목(t)이나 내용(c)에 '한글'이라는 키워드가 있는 글을 검색한다.
테스트를 진행하면 아래와 같은 쿼리가 실행된다.
쿼리의 where절 안쪽에서는 검색조건이 처리된다.
Hibernate:
select
guestbook0_.gno as gno1_0_,
guestbook0_.moddate as moddate2_0_,
guestbook0_.reg_date as reg_date3_0_,
guestbook0_.content as content4_0_,
guestbook0_.title as title5_0_,
guestbook0_.writer as writer6_0_
from
guestbook guestbook0_
where
guestbook0_.gno>?
and (
guestbook0_.title like ? escape '!'
or guestbook0_.content like ? escape '!'
)
order by
guestbook0_.gno desc limit ?
PREVfalse
NEXTfalse
TOTAL0
----------------------------------------------
gno > 0 조건은 바깥쪽으로 처리되었다.
'Framework > Spring' 카테고리의 다른 글
guestbook: 11. 검색처리(3) (0) | 2022.07.24 |
---|---|
guestbook : 11. 검색처리(2) (0) | 2022.07.23 |
guestbook : 10. guestbook 게시글 수정/삭제 처리(3) (0) | 2022.07.21 |
guestbook : 10. guestbook 게시글 수정/삭제 처리(2) (0) | 2022.07.21 |
guestbook : 10. guestbook 게시글 수정/삭제 처리(1) (0) | 2022.07.21 |
Comments