* 이 게시물에는
isBefore()
isAfter()
isEqual()
Fetch API
Mybatis select 관련 정보
MySQL SELECT 쿼리 정보
를 알 수 있습니다
영업부의 주문 관리 시스템을 만드는 기업 연계 프로젝트를 하면서 요구사항 중에 판매가 기간을 기간 별로 다르게 설정하여 주문을 할 때 납기 요청일에 따라 같은 제품이더라도 그 기간에 해당하는 가격만 나오게 해달라는 요구가 있었다
데이터베이스는 MariaDB를 썼고 우리의 판매가 기간 설정에 대한 데이터 베이스는 아래와 같다
KVV라는 바이어에게 GA0002 제품에 대한 주문이 있을 때 각 기간 별로 가격이 다른 것을 알 수 있다
해당 기능 시연 UI이다(UI가 구려보이는 건 내가 디자인 감각이 없기 때문이다..역시 나는 프론트보다는 그나마 백엔드가)
어쨌든 밑에 코드를 보자
<%-- 전체 제품 검색 --%>
document.querySelector("#allProductInfoBtn").addEventListener("click", function(){
//제품 검색에 사용된 검색어
const allProductInfo = document.querySelector("#allProductInfo").value;
//바이어 코드
const tableBuyerCode = document.querySelector("input[name='buyerCode']:checked").value;
//납기 요청일
const deliveryDate = document.querySelector("#deliveryDate").value;
if(deliveryDate != ''){
fetch(path+"/order/searchAllProductInfo/"+allProductInfo+"/"+tableBuyerCode+"/"+deliveryDate)
.then(res => res.json())
.then(list => {
document.querySelector("#productTable").innerHTML = "";
const table =
`<table class="table">
<thead>
<tr>
<th scope="col">선택</th>
<th scope="col">제품코드</th>
<th scope="col">제품그룹</th>
<th scope="col">제품명</th>
<th scope="col">규격(inch)</th>
<th scope="col">무게(lb)</th>
<th scope="col">단위</th>
<th scope="col">단가</th>
<th scope="col">판매가</th>
<th scope="col">판매 기간</th>
</tr>
</thead>
<tbody id="productBody">
</tbody>
</table>`
document.querySelector("#productTable").insertAdjacentHTML("afterbegin", table)
for(const item of list){
const p = item.price
let price = p.toLocaleString();
const sp = item.salePrice
let salePrice = sp.toLocaleString();
const productTableItem =
`<tr>
<th>
<input class="form-radio-input" type="checkbox" style="width : 20px; height : 20px;"
id="productCheckBox-\${item.productCode}" name="productCode" value="\${item.productCode}"
data-from-date = "\${item.fromDate}" data-end-date="\${item.endDate}">
</th>
<td>\${item.productCode }</td>
<td>\${item.productType }</td>
<td>\${item.productName }</td>
<td>\${item.size }</td>
<td>\${item.weight }</td>
<td>\${item.unit }</td>
<td>\${price}</td>
<td>\${salePrice }</td>
<td>\${item.fromDate}~\${item.endDate}</td>
</tr>`
document.querySelector("#productBody").insertAdjacentHTML("beforeend", productTableItem)
}
})
} else {
alert("납기 요청일을 입력해주세요.")
}
})
페이지 리로딩이 아닌 해당 페이지에서 바로 변경되게 만들려고 했기 때문에 fetch를 썼다 생각보다 테스트를 돌려보면서 납기 요청일을 깜빡하고 검색하는 경우가 많아서 if 문으로 먼저 납기 요청일이 입력되어 있는지 확인했고 없으면 alert를 띄웠다
해당 코드에서는 바이어코드(tableBuyerCode), 제품 검색어(allProductInfo), 납기 요청일(deliveryDate)을 @pathvariable로 보냈다 그리고 온 응답을 미리 만들어진 thead에 각각 붙여지도록 코드를 짰다
전체 검색 버튼을 누르면 이뤄지는 요청과 응답은 각각 이렇다
아까 말했듯이 요청 헤더의 각 변수를 @PathVariable에 담고 값이 잘 나오는 출력했다
@GetMapping("searchAllProductInfo/{allProductInfo}/{tableBuyerCode}/{deliveryDate}")
@ResponseBody
public List<ProductDto> searchAllProductInfo(@PathVariable String allProductInfo,
@PathVariable String tableBuyerCode, @PathVariable String deliveryDate) {
// 잘 들어오는 지 확인
System.out.println(service.searchProduct(allProductInfo, tableBuyerCode, deliveryDate));
return service.searchProduct(allProductInfo, tableBuyerCode, deliveryDate);
}
< Controller>
이제 요구사항을 만족시켜야 하는 서비스 단에서의 비즈니스 로직이다
먼저 mapper.getPriceDate를 통해서 GA0002에 해당하는 모든 판매가 기간을 가져와 List에 담았다(mapper.getPriceDate 코드 참고)
그러고 나서 for문을 돌리면서 리스트의 판매가 시작기간(fromDate)이 해당 납기 요청일과 같거(isEqual())나 전(isBefore())이고 종료 기간이(endDate) 해당 납기 요청일과 같거나 후이면 fromDate와 endDate를 사용하여 mapper.searchProduct()를 실행(mapper.searchProduct() 코드 참고)시켜서 ProductDto에 담았다
여담으로 ProductDto의 fromDate, endDate가 String인 이유는 다른 팀원들이 String이 편하다고 했기 때문이다 input type="date"의 value 값을 보내면 String으로 인식했기에 나중에 LocalDate로 파싱 하는게 더 편했다
public List<ProductDto> searchProduct(String allProductInfo, String tableBuyerCode, String deliveryDate) {
// TODO Auto-generated method stub
allProductInfo = "%" + allProductInfo + "%";
LocalDate parsedDeliveryDate = LocalDate.parse(deliveryDate, DateTimeFormatter.ISO_DATE);
List<ProductDto> dates = mapper.getPriceDate(allProductInfo, tableBuyerCode);
List<ProductDto> pddList = new ArrayList<>();
for (ProductDto date : dates) {
String fd = date.getFromDate();
String ed = date.getEndDate();
LocalDate fromDate = LocalDate.parse(fd, DateTimeFormatter.ISO_DATE);
LocalDate endDate = LocalDate.parse(ed, DateTimeFormatter.ISO_DATE);
if ((fromDate.isEqual(parsedDeliveryDate) || fromDate.isBefore(parsedDeliveryDate))
&& (endDate.isEqual(parsedDeliveryDate) || endDate.isAfter(parsedDeliveryDate))) {
pddList = mapper.searchProduct(allProductInfo, tableBuyerCode, fromDate, endDate);
System.out.println(allProductInfo + " " + fromDate + " " + endDate);
}
}
if (pddList.size() != 0) {
return pddList;
} else {
return null;
}
}
<Service 에서의 코드>
<!-- 판매가 날짜 판별 위한 날짜 데이터 추출 -->
<select id="getPriceDate" resultType="com.sharedOne.domain.master.ProductDto">
SELECT
s.productCode,
s.fromDate,
s.endDate
FROM SalePrice s
LEFT JOIN Product p ON s.productCode = p.productCode
WHERE buyerCode=#{tableBuyerCode} AND (p.productCode LIKE #{allProductInfo}
OR p.productName LIKE #{allProductInfo}
OR p.productType LIKE #{allProductInfo}
OR p.weight LIKE #{allProductInfo}
OR p.size LIKE #{allProductInfo}
)
</select>
<mapper.getPriceDate>
package com.sharedOne.domain.master;
import lombok.Data;
@Data
public class ProductDto {
//productCode, productName, productType, weight, size, price, unit, content, inserted
private String productCode;
private String productName;
private String productType;
private int weight;
private int size;
private int price;
private String unit;
private String content;
private String inserted;
private String modified;
private String fromDate;
private String endDate;
private String buyerCode;
// 테이블에서 조인해서 가져오려면 필요해서 넣었습니다
private int salePrice;
}
<ProductDto>
<select id="searchProduct" resultType="com.sharedOne.domain.master.ProductDto">
SELECT
p.productCode,
p.productType,
p.productName,
p.weight,
p.size,
p.price,
p.unit,
s.salePrice,
s.fromDate,
s.endDate
FROM
Product p
LEFT JOIN SalePrice s ON p.productCode = s.productCode
WHERE
(p.productCode LIKE #{allProductInfo}
OR p.productName LIKE #{allProductInfo}
OR p.productType LIKE #{allProductInfo}
OR p.weight LIKE #{allProductInfo}
OR p.size LIKE #{allProductInfo}
)
AND (s.fromDate = #{fromDate} AND s.endDate = #{endDate})
AND s.buyerCode = #{tableBuyerCode}
ORDER BY p.productCode
</select>
<mapper.searchProduct() 코드>
<느낀 점>
구글링을 하면서 여러 블로그 및 글들을 보면 내가 필요한 정보가 아닌 경우가 너무나도 많았고 내 상황에 해당하지 않는 오류 해결의 글도 너무나도 많았다 그래서 이번에 코드 리뷰를 하며 미리 위에 이 글을 읽게 될 사람들에게 어떤 정보를 얻을 수 있는지 명시를 해놨는데 그렇게 생각보다 효과적이지는 않았던 거 같다 나름 내가 사용한 모든 것을 잘 설명해 놓으려고 했는데 코드를 배치하는 순서도 그림의 순서도 설명도 내 생각만큼 딱딱 알아 볼 수 있게 적기가 쉽지 않았던 거 같다
어쨌든 이런 비기너의 시절의 코드를 나중에 돌아보면 여러 생각도 많이 들고 내가 어디까지 발전했는지 알 수 있겠지
기능적으로 아쉬운 점은 서비스에서 for문과 if문으로 거쳐가는 단계가 많아 느릴 수 있다는 점과 나중에 데이터가 많아질때 해당 제품의 판매가 기간이 10개가 설정되어 있다면 어쨌든 10개를 다 가져와 if문을 돌면서 일일이 비교해야 한다는 점이다 나중에 더 시야가 넓어지고 아는게 많아지면 더 나은 방법이 보일 것이다
*더 나은 방법과 따끔한 조언은 언제나 감사합니다