Kim VamPa

[Spring][쇼핑몰 프로젝트][31] 검색 구현(조건 검색 적용 -1) 본문

스프링 프레임워크/쇼핑몰 프로젝트

[Spring][쇼핑몰 프로젝트][31] 검색 구현(조건 검색 적용 -1)

Kim VamPa 2021. 8. 26. 17:05
728x90
반응형
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188

 

목표

조건 검색을 위해 기존 검색 쿼리를 동적 쿼리로 변환

 

 저번 포스팅에서 구현한 기능은 '책 제목'에 관해서만 검색이 되도록 되어 있습니다. 이번 포스팅에선 기존 검색 쿼리가 '작가 검색', '카테고리 검색', '카테고리 + 작가', '카테고리 + 책 제목' 검색이 되도록 변경하는 것이 목표입니다.

 

 

 

 

순서

1. 방향

2. Criteria 클래스

 

 

 

 

 

1. 방향

  구현의 가장 간단한 방법은 검색 조건에 맞는 쿼리문을 모두 일일이 작성을 해두고 사용자가 요청하는 조건에 따라서 해당 쿼리문을 요청하는 것입니다. 해당 방식은어떠한 조건이 있는지 Controller 혹은 Service에서 판단하여 필요로 한 쿼리문을 요청하게 될 것입니다.

 

그림 1-1

 

 하지만 이럴 경우 메모리가 낭비되고, 코드를 관리하기가어렵다는 단점이 있습니다. 아래의 쿼리문 3개를 살펴보겠습니다. 각 쿼리문은 '책 제목 검색', '작가 검색', '책 제목 + 카테고리 검색'입니다.

 

그림 1-2

 

그림 1-3

 

그림 1-4

 

 위의 코드를 보시다시피 변경되는 부분은 코드의 일부분입니다. 조금의 다른 부분 때문에 다른 동일한 코드들을 반복해서 작성을 해야만 합니다. 만약 쿼리 문의 경과 데이터를 변경하기 위해서 select 부분의 칼럼명을 수정해주어야 하는 상황이라면 모든 쿼리문을 일일이 수정을 해주어야만 할 것입니다. 

 

 이러한 문제점을 피하기 위해서 우리는 MyBatis에서 제공해주는 기능을 사용하여 쿼리문이 상황에 따라 동적으로 변경되도록 변경할 것입니다. 앞서 말한 정적 쿼리 문의 경우 판단의 근거가 되는 데이터를 활용하여 Service 혹은 Controller에서 필요로 한 쿼리문들을 요청하는 방식이었지만, 우리가 구현할 동적 쿼리의 경우 판단의 근거가 되는 데이터를 직접 쿼리문에 넘겨줄 것입니다.

 

그림 1-5

 

 MyBastis의 기능들은 따로 정리를 해두었기 때문에 설명을 덧붙이지는 않을 것입니다. 

 

[MyBatis] <sql>, <include> 사용법

[MyBatis] <choose>, <when>, <otherwise>

[MyBatis] <trim> 사용법

[MyBatis] <foreach> 사용법

 

 

2. Criteria 클래스

 추가적인 데이터들을 Mapper에 전달하기 위해서, 쿼리문에 생성에 필요로 한 데이터를 전달하는 용도로 생성한 Criteria 클래스에 몇 가지 변수와 메서드를 추가해줄 것입니다. 

 

 새로운 코드를 추가하기 앞서서 어떠한 쿼리로 작성되어야 할지 판단의 근거가 되는 데이터를 가지는 type 변수와 getTypeArr() 메서드에 대해 먼저 살펴보겠습니다.

 

그림 2-1

 

 type 변수는 사용자가 어떠한 검색을 하는지에 대한 데이터를 저장합니다. 작가 검색은 "A", 책 제목 검색은 "T", 카테고리 검색은 "C"로 로 정하였는데, 사용자가 어떠한 검색을 하냐에 따라 앞서 말한 String 데이터의 조합을 저장하게 됩니다.

 

 예를 들어서 사용자가 "책 제목"만 검색을 할 경우 type은 "T"를 저장하게 되고, 사용자가 "책 제목 + 카테고리" 조건의 검색을 할 경우 "TC"를 전달받게 될 것입니다.

 

 

 다음으로 알아볼 것은 getTypeArr() 메서드입니다. type 변수에 담긴 값을 MyBatis의 Mapper에 그대로 전달하면 활용을 할 수가 없습니다. 활용을 하기 위해선 "T", "C", "G" 하나하나의 String값이 필요로 합니다.  따라서 뷰로부터 전달받은 type의 값을 String 클래스의 split() 메서드를 사용하여 String 배열 형태로 Mapper에 전달하게 됩니다.

 

 그렇다면 typeArr 변수와 Getter/Setter 메서드를 작성하지 않고 Getter 메서드에 해당하는 getTypeArr() 메서드만 작성했는지 의아해하실 수도 있습니다. 그 이유는 MyBatis에서 값을 가져올 때 Getter메서드의 형태로 필요로 한 값을 가져오기 때문입니다. type 변수에 저장된 값을 단지 배열 형태로 가져만 오면 돼서 typeArr 변수와 Setter 메서드를 따로 사용할 상황이 없기 때문에 Getter 메서드만 작성해주었습니다. 

 

 

 지금부터는 새로 추가할 변수와 메서드입니다. 변수로 authorArr와 cateCode를 작성하고, 그 변수의 Getter, Setter 메서드를 작성해줍니다. 

 

cateCode의 경우 사용자가 요청하는 카테고리 번호를 저장하기 위함입니다.

 

 authorArr의 경우 String 배열 타입으로 선언을 하였는데 그다음 순서에서 이유를 말하겠습니다.

 

	/* 작가 리스트 */
	private String[] authorArr;
	
	/* 카테고리 코드 */
	private String cateCode;
    
	public String[] getAuthorArr() {
		return authorArr;
	}

	public void setAuthorArr(String[] authorArr) {
		this.authorArr = authorArr;
	}

	public String getCateCode() {
		return cateCode;
	}

	public void setCateCode(String cateCode) {
		this.cateCode = cateCode;
	}

	@Override
	public String toString() {
		return "Criteria [pageNum=" + pageNum + ", amount=" + amount + ", type=" + type + ", keyword=" + keyword
				+ ", authorArr=" + Arrays.toString(authorArr) + ", cateCode=" + cateCode + "]";
	}

 

그림 2-2

 

그림 2-3

 

 

3. Mapper 메서드

 

 본격적으로 기존 쿼리문을 동적 쿼리문으로 변경하기 앞서서 '작가 검색'을 위해 필요로 한  authorId 정보를 만들어 내는 Mapper 메서드를 먼저 작성해 줄 것입니다.

 

 홈페이지 사용자는 자신이 원하는 작가의 '이름'을 작성하여 서버로 보내게 됩니다. vam_book 테이블에 authorName의 컬럼이 있다면 기존 검색 쿼리에 전달받은 '이름' 데이터를 그대로 사용하면 될 것입니다. 하지만 현재의 vam_book 테이블에는 'authorName' 대신 'authorId'의 컬럼만 존재합니다. 따라서 사용자가 작성하여 서버로 전송한 '작가 이름'을 'authorId'로 변환해주는 작업을 해주는 Mapper 메서드를 작성할 것입니다.

 

그림 3-1

 

 작성해줄 Mapper 메서드의 반환 결과로 전달받은 데이터를 Criteria의 authorArr 변수에 저장해줄 것입니다. 그런데 authorArr은 String 배열 타입으로 왜 선언하였는지 의문이 들 수 있습니다. 이는 검색 결과 authirId가 한 개가 아닌 경우가 있을 수 있기 때문입니다. 예를 들어 "유홍준"이라는 이름을 검색을 했을대 같은 이름의 작가가 2명 이상일 경우도 있고, "유홍" 키워드만으로 검색을 할 경우 이름에 "유홍"을 가진 authorId를 모두 반환할 수 있기 때문입니다.

 

 BookMapper.java 인터페이스 아래의 메서드 선언문을 추가해줍니다.

 

	/* 작가 id 리스트 요청 */
	public String[] getAuthorIdList(String keyword);

 

 

 

 BookMapper.xml 파일에 앞서 선언한 메서드가 실행할 쿼리문을 작성합니다.

 

	<-- Oracle -->
    <!-- 작가 id 리스트 요청 -->
	<select id="getAuthorIdList" resultType="String">
	
		select authorid from vam_author where authorName like '%' || #{keyword} || '%'
	
	</select>
    
    <-- MySQL -->
    <!-- 작가 id 리스트 요청 -->
	<select id="getAuthorIdList" resultType="String">
	
		select authorid from vam_author where authorname like concat ('%', #{keyword}, '%')
	
	</select>

 

그림 3-3 Oracle

 

그림 3-3(1) MySQL

 

 

 BookMapperTests.java 클래스에 작성한 메서드가 정상적으로 동작하는지 확인을 하기 위해 Junit 테스틀 진행합니다.

 

	/* 작가 id 리스트 요청 */
	
	@Test
	public void getAuthorId() {
		
		String keyword = "폴";
		
		String[] list = mapper.getAuthorIdList(keyword);
		
		System.out.println("결과 : " + list.toString());
		
		for(String id : list) {
			System.out.println("개별 결과 : " + id);
		}
		
		
	}

 

그림 3-4

 

 

4. Service 메서드

 기존의 검색 쿼리를 요청하던 Service 메서드에 작성한 메서드를 적용해주겠습니다. 새로 작성한 메서드를 getGoodsList(), goodsGetTotal() 메서드 두 개 모두 적용해주어도 상관은 없습니다. 그런데 두 개의 메서드는 보통 Controller에서 거의 같이 사용이 되며, getGoodsList() 메서드를 호출 한 뒤 goodsGetTotal() 호출하게 되어 있습니다. 새로 작성한 메서드는 Criteria 객체의 authorArr 값을 저장하게 해주는 역할인데, getGoodsList() Service 메서드에서 Criteria 객체에 authorArr이 부여되면 이 객체를 그대로 goodsGetTotal()에서 사용하기 때문에 getGoodsList() 메서드에만 새로운 Mapper 메서드를 적용해주겠습니다.

 

그림 4-1

 

 새로운 코드는 return 코드의 앞 공간에 작성합니다. 아래의 코드를 추가해주어서 Criteria의 authorArr 변수에 값을 부여해주도록 하였습니다.

 

				String[] authorArr = bookMapper.getAuthorIdList(cri.getKeyword());
				cri.setAuthorArr(authorArr);

 

 

 그런데 이 코드는 오직 검색 조건에 '작가'에 대한 검색이 있을 때만 숭행 하면 됩니다. 따라서 조건을 부여하여 해당 코드가 '작가'일 때만 실행이 되도록 코드를 추가해주었습니다.

 

		String type = cri.getType();
		String[] typeArr = type.split("");
		
		for(String t : typeArr) {
			if(t.equals("A")) {
				String[] authorArr = bookMapper.getAuthorIdList(cri.getKeyword());
				cri.setAuthorArr(authorArr);
			}
		}

 

그림 4-2

 

 

 다음 포스팅에서 본격적으로 동적 쿼리를 작성해보도록 하겠습니다. 

 

REFERENCE

  • 코드로배우는 스프링 웹 프로젝트(남가람북스)
  •  

 

 

 

DATE

  • 2020.08.26

 

728x90
반응형
Comments