Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e4f2749
🛠️ refactor: 옵션 반환타입 수정
KWAK-JINHO Apr 8, 2025
48fe4fc
🛠️ refactor: fetchCount() deprecated로 인한 수정
KWAK-JINHO Apr 10, 2025
9822ddb
🛠️ refactor: fetchOne optional처리
KWAK-JINHO Apr 10, 2025
2bf090f
🛠️ refactor: product 엔티티 수정
KWAK-JINHO Apr 10, 2025
ecb7968
🔧 chore: macOS 경고 수정
KWAK-JINHO Apr 10, 2025
9a190b1
🛠️ refactor: 카테고리 조회 트랜잭션 readOnly로 변경
KWAK-JINHO Apr 12, 2025
795438b
🛠️ refactor: 상품 조회 요청 dto 수정
KWAK-JINHO Apr 12, 2025
1a98ac1
🛠️ refactor: QClass 생성 위치 지정
KWAK-JINHO Apr 12, 2025
af70da9
🛠️ refactor: 상품 목록 조회 리펙토링
KWAK-JINHO Apr 14, 2025
bb0c689
🛠️ refactor: ProductRepositoryQueryDslImpl 리펙토링
KWAK-JINHO Apr 14, 2025
554e1c3
🛠️ refactor: 별점필터링 에러 수정
KWAK-JINHO Apr 14, 2025
679cc0f
🛠️ refactor: 카테고리 헤더 상품목록 조회 리펙토링
KWAK-JINHO Apr 14, 2025
0c30ca3
🛠️ refactor: 상품목록조회 동적쿼리 수정
KWAK-JINHO Apr 14, 2025
0a5e1d3
🔧 chore: 기타 정리
KWAK-JINHO Apr 14, 2025
f5d8b78
🛠️ refactor: 카테고리필터링 시 못불러오던 오류 수정
KWAK-JINHO Apr 14, 2025
e483a0a
🛠️ refactor: ProductService 카테고리 메서드 책임에 맞게 이동
KWAK-JINHO Apr 14, 2025
9092b4f
🛠️ refactor: 상품 목록 조회 null 처리 레포지토리에서 하도록 변경
KWAK-JINHO Apr 14, 2025
a262264
🛠️ refactor: 헤더 카테고리 상품조회 size범위 고정
KWAK-JINHO Apr 14, 2025
b0461d2
🔧 chore: 매개변수 명 오타 수정
KWAK-JINHO Apr 14, 2025
09b1f70
🛠️ refactor: 오늘의 특가 기능 리펙토링
KWAK-JINHO Apr 14, 2025
671b0fc
🔧 chore: 컨벤션 수정
KWAK-JINHO Apr 15, 2025
95d8454
🔧 chore: 주석 및 공백 삭제
KWAK-JINHO Apr 15, 2025
21fcd08
🛠️ refactor: option dto 수정
KWAK-JINHO Apr 15, 2025
a808c50
🛠️ refactor: embed 분리에 따른 dto 생성
KWAK-JINHO Apr 15, 2025
c7fc814
🛠️ refactor: null 필터링 위한 List 사용
KWAK-JINHO Apr 15, 2025
1a9d4ba
Merge branch 'develop' into be/refactor/294
KWAK-JINHO Apr 16, 2025
32cf1b1
Merge branch 'develop' into be/refactor/294
KWAK-JINHO Apr 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion backend/JiShop/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies {

// Querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

Expand Down Expand Up @@ -80,6 +80,9 @@ dependencies {
implementation 'org.springframework.session:spring-session-data-redis'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'

// Apple Silicon native DNS resolver
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.100.Final:osx-aarch_64'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 뭔가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macOS 환경에서 Netty가 DNS를 해석하지 못하고 생기는 경고가 있었습니다. 실행에는 문제 없었으나 프로그램 실행시 나오는 경고를 해결하기 위해서 추가한 의존성입니다.


// Logstash를 위한 logstash-logback-encoder
// implementation 'net.logstash.logback:logstash-logback-encoder:7.2'

Expand All @@ -97,3 +100,13 @@ dependencyManagement {
tasks.named('test') {
useJUnitPlatform()
}

def generated = 'build/generated/sources/annotationProcessor/java/main'

tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}

clean {
delete file(generated)
}
Comment on lines +104 to +112
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 뭔가요? 설명해주세요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Querydsl을 사용함에 따라 QClass 파일이 생기는데 IDE 설정마다 해당파일이 생기는 경로가 다르게 설정됩니다. 따라서 해당경로가 생기는 위치를 통일하기 위해서 추가한 설정입니다.
clean은 gradle clean시 해당 디렉토리를 명시적으로 삭제하기 위해서 추가했습니다.

Empty file modified backend/JiShop/gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,37 @@ public static CartDetailResponse from(
return new CartDetailResponse(
cartId,
product.getId(),
product.getProduct().getName(),
product.getProduct().getProductInfo().getName(),
product.getOption() != null ? product.getOption().getOptionValue() : "기본옵션",
paymentPrice,
orderPrice,
discountPrice,
quantity,
paymentPrice * quantity,
product.getProduct().getMainImage(),
product.getProduct().getBrand(),
product.getProduct().getImage().getMainImage(),
product.getProduct().getProductInfo().getBrand(),
Comment on lines +32 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

embeded로 info 만들었다면 보내실때도 객체 dto로 묶어서 보내면 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 DTO를 만들어 놓겠습니다.. 추후에 원하시는 분들께서는 사용해주시면 좋을것 같습니다.. 모든 파일을 까볼수가 없기때문에 😭

isExisted
);
}

public static CartDetailResponse of(Cart cart, boolean isExisted) {
SaleProduct saleProduct = cart.getSaleProduct();
int paymentPrice = saleProduct.getProduct().getDiscountPrice();
int orderPrice = saleProduct.getProduct().getOriginPrice();
int paymentPrice = saleProduct.getProduct().getProductInfo().getDiscountPrice();
int orderPrice = saleProduct.getProduct().getProductInfo().getOriginPrice();
int discountPrice = orderPrice - paymentPrice;

return new CartDetailResponse(
cart.getId(),
saleProduct.getId(),
saleProduct.getProduct().getName(),
saleProduct.getProduct().getProductInfo().getName(),
saleProduct.getOption() != null ? saleProduct.getOption().getOptionValue() : "기본옵션",
paymentPrice,
orderPrice,
discountPrice,
cart.getQuantity(),
paymentPrice * cart.getQuantity(),
saleProduct.getProduct().getMainImage(),
saleProduct.getProduct().getBrand(),
saleProduct.getProduct().getImage().getMainImage(),
saleProduct.getProduct().getProductInfo().getBrand(),
isExisted
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,16 @@ public CartResponse getGuestCart(List<GuestCartRequest> guestCartRequests) {
return new CartDetailResponse(
null, //장바구니 ID는 null (비회원이니까)
saleProduct.getId(),
saleProduct.getProduct().getName(),
saleProduct.getProduct().getProductInfo().getName(),
saleProduct.getOption() != null ? saleProduct.getOption().getOptionValue() : "기본옵션",
saleProduct.getProduct().getDiscountPrice(),
saleProduct.getProduct().getOriginPrice(),
saleProduct.getProduct().getOriginPrice() - saleProduct.getProduct().getDiscountPrice(),
saleProduct.getProduct().getProductInfo().getDiscountPrice(),
saleProduct.getProduct().getProductInfo().getOriginPrice(),
saleProduct.getProduct().getProductInfo().getOriginPrice() -
saleProduct.getProduct().getProductInfo().getDiscountPrice(),
quantity,
saleProduct.getProduct().getDiscountPrice() * quantity,
saleProduct.getProduct().getMainImage(),
saleProduct.getProduct().getBrand(),
saleProduct.getProduct().getProductInfo().getDiscountPrice() * quantity,
saleProduct.getProduct().getImage().getMainImage(),
saleProduct.getProduct().getProductInfo().getBrand(),
Comment on lines +136 to +145
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위와 동일합니다.

false
);
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.jishop.category.controller;

import com.jishop.category.dto.CategoryResponse;
import com.jishop.product.dto.response.ProductResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.data.web.PagedModel;

import java.util.List;

@Tag(name = "카테고리 API")
public interface CategoryController {

PagedModel<ProductResponse> getProductListByCategory(Long CategoryId, int page);

@Operation(summary = "최상위 카테고리의 ID와 이름 조회")
List<CategoryResponse> getCategoryFilterInfo();

@Operation(summary = "해당 카테고리의 한단계 하위카테고리 ID 조회")
List<CategoryResponse> getSubcategoriesByParentId(Long categoryId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import com.jishop.category.dto.CategoryResponse;
import com.jishop.category.service.CategoryService;
import com.jishop.product.dto.response.ProductResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.web.PagedModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -19,17 +17,6 @@ public class CategoryControllerImpl implements CategoryController {

private final CategoryService categoryService;

@Override
@GetMapping("/products")
public PagedModel<ProductResponse> getProductListByCategory(
@RequestParam(defaultValue = "50000000L") Long categoryId,
@RequestParam(defaultValue = "0") int page) {
if (page < 0 || page > 100) {page = 0;}

return categoryService.getProductsByCategory(categoryId, page);
}

// 진입 시점에 부르면 내려감 헤더용
@Override
@GetMapping("/root")
public List<CategoryResponse> getCategoryFilterInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ public interface CategoryRepository extends JpaRepository<Category, String> {

/**
* 상위(1뎁스) 카테고리 ID로 그 하위(2,3뎁스) 카테고리 상품 목록 모두 조회
* @param CategoryId 1뎁스 카테고리 ID
* @param categoryId 1뎁스 카테고리 ID
* @return 해당 카테고리에 속한 상품 페이지
*/
@Query(value = "SELECT p FROM Product p " +
"LEFT JOIN p.category c1 " +
"LEFT JOIN Category c2 ON c1.parent = c2 " +
"WHERE c1.parent.currentId = :CategoryId OR " +
"c2.parent.currentId = :CategoryId",
"WHERE c1.parent.currentId = :categoryId OR " +
"c2.parent.currentId = :categoryId",
countQuery = "SELECT COUNT(p) FROM Product p " +
"LEFT JOIN p.category c1 " +
"LEFT JOIN Category c2 ON c1.parent = c2 " +
"WHERE c1.parent.currentId = :CategoryId OR " +
"c2.parent.currentId = :CategoryId")
"WHERE c1.parent.currentId = :categoryId OR " +
"c2.parent.currentId = :categoryId")
Page<Product> findProductsByCategoryWithAllDescendants(
@Param("CategoryId") Long CategoryId, Pageable pageable);
@Param("categoryId") Long categoryId, Pageable pageable);

/**
* 특정 카테고리의 모든 하위 카테고리 ID를 재귀적으로 조회
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package com.jishop.category.service;

import com.jishop.category.dto.CategoryResponse;
import com.jishop.product.dto.response.ProductResponse;
import org.springframework.data.web.PagedModel;

import java.util.List;

public interface CategoryService {

PagedModel<ProductResponse> getProductsByCategory(Long categoryId, int page);

List<CategoryResponse> getCategoryFilterInfo();

List<CategoryResponse> getSubcategoriesByParentId(Long categoryId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,20 @@
import com.jishop.category.dto.CategoryResponse;
import com.jishop.category.repository.CategoryRepository;
import com.jishop.category.service.CategoryService;
import com.jishop.product.domain.Product;
import com.jishop.product.dto.response.ProductResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.*;
import org.springframework.data.web.PagedModel;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@Transactional
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class CategoryServiceImpl implements CategoryService {

private final CategoryRepository categoryRepository;
// private final CategoryRedisService categoryRedisService;

@Override
public PagedModel<ProductResponse> getProductsByCategory(Long categoryId, int page) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 조회기능은 완전 없어진건가요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상품 조회 기능이 카테고리 디렉토리에 있는것이 이상해서 상품 디렉토리로 가져왔습니다!


Pageable pageable = PageRequest.of(page, 12, Sort.by(Sort.Direction.DESC, "wishListCount"));
Page<Product> productPage = categoryRepository.findProductsByCategoryWithAllDescendants(categoryId, pageable);

List<ProductResponse> productsResponse = productPage.getContent().stream()
.map(ProductResponse::from)
.toList();

return new PagedModel<>(new PageImpl<>(
productsResponse,
pageable,
productPage.getTotalElements()
));
}

@Override
public List<CategoryResponse> getCategoryFilterInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ public static class ProductResponse {
public static ProductResponse from(Product product) {
return new ProductResponse(
product.getId(),
product.getName(),
product.getMallSeq(),
product.getBrand(),
product.getDescription(),
product.getOriginPrice(),
product.getDiscountPrice(),
product.getDiscountRate(),
product.getMainImage()
product.getProductInfo().getName(),
product.getProductInfo().getMallSeq(),
product.getProductInfo().getBrand(),
product.getProductInfo().getDescription(),
product.getProductInfo().getOriginPrice(),
product.getProductInfo().getDiscountPrice(),
product.getProductInfo().getDiscountRate(),
product.getImage().getMainImage()
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,22 @@
public record FashionClothesOptionResponse(
List<ColorSizeOption> option
) {
public static FashionClothesOptionResponse from(List<Map<String, Object>> productOptions) {
public static FashionClothesOptionResponse from(List<SizeOption> productOptions) {
if (productOptions == null || productOptions.isEmpty()) {
return new FashionClothesOptionResponse(List.of());
}

Map<String, List<SizeOption>> fashionClothesOptions = new HashMap<>();

for (Map<String, Object> option : productOptions) {
String optionValue = (String) option.get("optionValue");
String[] colorAndSize = optionValue.split("/");

if (colorAndSize.length == 2) {
String color = colorAndSize[0];
String size = colorAndSize[1];
for (SizeOption option : productOptions) {
if (option.isValidOption()) {
String color = option.extractColor();

if (!fashionClothesOptions.containsKey(color)) {
fashionClothesOptions.put(color, new ArrayList<>());
}
SizeOption sizeOption = new SizeOption(
(Long) option.get("saleProductId"),
size,
(int) option.get("optionExtra")
);

SizeOption sizeOption = option.withSize();
fashionClothesOptions.get(color).add(sizeOption);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public record GeneralOptionResponse(
List<ProductOption> options
) {
public static GeneralOptionResponse from(final List<Map<String, Object>> productOptions) {
public static GeneralOptionResponse from(final List<SizeOption> productOptions) {
final List<ProductOption> generalOptions = new ArrayList<>();

for (final Map<String, Object> option : productOptions) {
for (final SizeOption option : productOptions) {
ProductOption productOption = new ProductOption(
(Long) option.get("saleProductId"),
(String) option.get("optionValue"),
(int) option.get("optionExtra")
option.saleProductId(),
option.optionValue(),
option.optionExtra()
Comment on lines 13 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option을 안넣고 필드로 넣으신 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option객체를 생성해서 직접 값을 주입하지 않았냐고 여쭤보시는게 맞을까요?
객체 생성을하지 않고 값 전달을 위해 필요한 필드만 뽑아 내는것이 좋다고 생각했습니다.

);
generalOptions.add(productOption);
}
Expand Down
21 changes: 21 additions & 0 deletions backend/JiShop/src/main/java/com/jishop/option/dto/SizeOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,25 @@ public record SizeOption(
String optionValue,
int optionExtra
) {
public boolean isValidOption() {
return (optionValue != null) && optionValue.split("/").length == 2;
}

public String extractColor() {
String[] colorAndSize = optionValue.split("/");
return colorAndSize[0];
}

public String extractSize() {
String[] colorAndSize = optionValue.split("/");
return colorAndSize[1];
}

public SizeOption withSize() {
return new SizeOption(
saleProductId(),
extractSize(),
optionExtra()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public OrderDetail(Order order, String orderNumber, SaleProduct saleProduct, int
}

public static OrderDetail from(Order order, SaleProduct saleProduct, int quantity){
int orderPrice = saleProduct.getProduct().getOriginPrice();
int paymentPrice = saleProduct.getProduct().getDiscountPrice();
int orderPrice = saleProduct.getProduct().getProductInfo().getOriginPrice();
int paymentPrice = saleProduct.getProduct().getProductInfo().getDiscountPrice();
Comment on lines +56 to +57
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 이 부분이 메소드 체이닝의 문제라고 생각듭니다. 무언가가 변경되었을때 다른 곳에서도 변경이 되니까요..
이 경우에는 어쩔 수 없지만 추후에 필드가 추가되서 변경이 가해지면 사이드 임펙트가 더 커질텐데 어떻게 하는게 맞을까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getProductInfo에 필드가 많아져서 또한번 embed로 만든다면?

Copy link
Contributor Author

@KWAK-JINHO KWAK-JINHO Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 이번에 embed를 분리하면서 생긴 사이드 이펙트를 겪고 주신 의견에 매우 큰 공감을 합니다..
현재 위의 OrderDetail의 from과 같은 메서드들은 디미터의 법칙을 준수하지 못하고 있다고 생각합니다. saleProduct의 내부구조를 OrderDetail은 몰라야 하지 않을까요? 하지만 설계단계부터 명확한 책임분리를 잡으면서 가지 못했기때문에 어쩔 수 없는 현상이라고 생각합니다..

int discountPrice = orderPrice - paymentPrice;

if(saleProduct.getOption() != null){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static OrderProductResponse from(OrderDetail detail, boolean canReview) {
return new OrderProductResponse(
detail.getId(),
detail.getSaleProduct().getId(),
detail.getSaleProduct().getProduct().getMainImage(),
detail.getSaleProduct().getProduct().getImage().getMainImage(),
detail.getSaleProduct().getName(),
detail.getSaleProduct().getOption() != null ? detail.getSaleProduct().getOption().getOptionValue() : null,
detail.getPaymentPrice(),
Expand All @@ -29,7 +29,7 @@ public static OrderProductResponse from(OrderDetail detail, boolean canReview) {
detail.getQuantity(),
detail.getPaymentPrice() * detail.getQuantity(),
canReview,
detail.getSaleProduct().getProduct().getBrand()
detail.getSaleProduct().getProduct().getProductInfo().getBrand()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ public CartResponse getCheckout(User user, List<OrderDetailRequest> orderDetailR
.map(OrderDetailRequest::quantity)
.orElseThrow(() -> new DomainException(ErrorType.INVALID_QUANTITY)); // 기본값은 1로 설정

int paymentPrice = product.getProduct().getDiscountPrice();
int orderPrice = product.getProduct().getOriginPrice();
int paymentPrice = product.getProduct().getProductInfo().getDiscountPrice();
int orderPrice = product.getProduct().getProductInfo().getOriginPrice();
int discountPrice = orderPrice - paymentPrice;

return CartDetailResponse.from(
Expand Down
Loading