Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

전남대 BE_나제법 5주차 과제(2단계) #303

Open
wants to merge 84 commits into
base: nove1080
Choose a base branch
from

Conversation

nove1080
Copy link

@nove1080 nove1080 commented Jul 26, 2024

궁금한 점

1. Service 에서 다른 Service 클래스를 참조하는 경우가 많아졌는데 어떡하면 좋을까요?

지금 각각 도메인에 대한 Service가 특정 Controller에만 적합하게 만들어두어서 서비스 계층 간의 참조에 있어서 사용하기 불편하게 되었는데

예를 들면, OrderService 에서 ProductOptionService의 기능을 이용하고자 할 때,

@Service
@Transactional(readOnly = true)
public class OrderService {

    private final ProductOptionService productOptionService;  //서비스 계층 간 참조
    private final ProductService productService;  //서비스 계층 간 참조

}

파라미터와 리턴타입이 Controller에만 핏하게 맞춰져있어 DTO에서 다시 DTO로 변환하는 등 불편한 점이 생겼습니다.
우선은 오버로딩을 통해 해결하였지만 이와 같이 특정 서비스가 여러 서비스에서 참조될 수 있는 범용적인 서비스 클래스가 된다면 그때마다 메서드를 추가하기도 곤란할 것 같습니다.

public class ProductOptionService {

    //사용하고자 하는 기능
    @Transactional
    public SubtractProductOptionQuantityResponse subtractOptionStock(Long optionId, SubtractProductOptionQuantityRequest request) {
        ProductOption option = productOptionRepository.findById(optionId)
            .orElseThrow(() -> new ResourceNotFoundException("상품 옵션", optionId.toString()));

        option.subtractQuantity(request.getQuantity());

        return SubtractProductOptionQuantityResponse.fromEntity(option);
    }
    
    //OrderService 가 사용하기 편리하도록 오버로딩
    public SubtractProductOptionQuantityResponse subtractOptionStock(CreateOrderRequest request) {
        return subtractOptionStock(request.getOptionId(), new SubtractProductOptionQuantityRequest(request.getQuantity()));
    }
}

이런 경우는 어떻게 대처할 수 있을까요? 범용적인 서비스 클래스를 만든 것부터 설계가 잘못된 걸까요?

과제를 하며 어려웠던 점

1. 카카오 메시지 API 사용이 어려웠습니다

@FeignClient(name = "kakaoClient")
public interface KakaoClient {
    @PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    KakaoMessageResult sendMessage(
        URI uri,
        @RequestHeader(AUTHORIZATION) String accessToken,
        @RequestBody String templateObject //toJson()의 반환값이 전달됩니다.
    );
}
    public String toJson() {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return "template_object=" + mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("JSON 변환 실패", e);
        }
    }

여기서 객체를 Json으로 변환할 때 "template_object=" 를 붙이지 않으면 에러가 나는데 왜 발생하는 건지 잘 모르겠습니다. 우선은 같은 문제를 겪은 글 https://devtalk.kakao.com/t/template-object-cant-be-null/116984 을 참고하여 해결방법은 찾았지만 왜 발생하는 건지 잘 모르겠습니다 혹시 멘토님은 아시나요?

nove1080 added 30 commits July 25, 2024 17:56
- /api/login/oauth2/kakao 추가
- LoginRequest.java
- Password.java
- schema.sql
- @ConfigurationPropertiesScan
- @EnableFeignClients
- Member의 password 필드 삭제로 인한 테스트 코드 수정이 필요함
}
}

public String getContentDescription() {
Copy link
Author

Choose a reason for hiding this comment

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

내부 클래스의 값을 사용할 일이 있으면 이런식으로 사용하게 되는건가요? 아니면 외부 클래스로 빼는게 더 나은가요?

Choose a reason for hiding this comment

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

혹시 이게 어떤말이죠?

Copy link
Author

Choose a reason for hiding this comment

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

지금 KakaoCommerce 라는 클래스 안에 내부 클래스로 Content 클래스가 존재하는데
내부 클래스의 참조는 해당 클래스를 선언한 클래스 즉, KakaoCommerce 안에서만 사용하도록 권장한다고 알고있는데

만약 KakaoCommerce 를 제외한 다른 외부 클래스에서 Content클래스의 필드를 참조할 일이 생긴다면 Content를 외부클래스로 선언하여야 하나요? 아니면 지금과 같이 getter를 kakaoCommerce 클래스에서 만들어서 사용하면 될까요?

Copy link
Author

Choose a reason for hiding this comment

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

#303 (comment)
동건님 이 부분도 한번 봐주시면 감사하겠습니다!

Copy link

@Hongdonggeon Hongdonggeon left a comment

Choose a reason for hiding this comment

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

안녕하세요 제법님
구현 잘해주셨는데요!
2가지 고민해보면 좋을 것 같아요
현재 주문 관련 엔티티가 없는 것 같고요
주문 할때 카카오톡 메시지 전송하는 로직까지 한 트랜잭션에 묶여있는데, 이 부분에 대해 고민해보면 좋을 것 같아요. 코멘트 남겨놓았습니다!

ReadProductResponse product = productService.readProductById(productId);
KakaoCommerce kakaoCommerce = KakaoCommerce.of(product, request.getMessage());

sendOrderMessageIfSocialMember(accessToken, kakaoCommerce);

Choose a reason for hiding this comment

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

카카오톡 메시지 전송하는건 같은 트랜잭션에 묶일 필요가 있을까요?
주문 및 수량 차감은 핵심 로직인데, 카카오톡 메시지 전송 api 가 실패해서 주문까지 실패하면 안될 . 것같아요
외부 api를 사용할 때는 이런 트랜잭션에 대해서 잘 고려해야됩니다!

Choose a reason for hiding this comment

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

혹시 그리고 주문 데이터는 따로 저장안할까요?

Copy link
Author

@nove1080 nove1080 Jul 30, 2024

Choose a reason for hiding this comment

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

혹시 그리고 주문 데이터는 따로 저장안할까요?

과제 진행이 좀 늦어져서 제출기한을 고려할 때 빠듯할 것 같아서 주문 데이터를 저장하는 부분은 만들지 않았습니다! 이제 만들도록 하겠습니다

Copy link
Author

Choose a reason for hiding this comment

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

카카오톡 메시지 전송하는건 같은 트랜잭션에 묶일 필요가 있을까요? 주문 및 수량 차감은 핵심 로직인데, 카카오톡 메시지 전송 api 가 실패해서 주문까지 실패하면 안될 . 것같아요 외부 api를 사용할 때는 이런 트랜잭션에 대해서 잘 고려해야됩니다!

이 부분은 생각하지 못했네요 아무 생각없이 트랜잭션으로 묶었던 것 같습니다

Copy link
Author

@nove1080 nove1080 Jul 30, 2024

Choose a reason for hiding this comment

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

안녕하세요 동건님! 리팩토링을 하는 과정에서 궁금한 부분이 있어서 여쭤보고 싶습니다!

@Transactional
public OrderResponse createOrder(String accessToken, Long productId, Long memberId, CreateOrderRequest request) {
    //상품 옵션 수량 차감
    productOptionService.subtractOptionStock(request);

    //주문 정보 저장
    Order order = orderRepository.save(request.toEntity(memberId, productId));

    //카카오 메시지 전송
    sendOrderMessageIfSocialMember(accessToken, productId, request);
    return OrderResponse.from(order);
}

주문 도메인이 추가되면서 주문 생성 시, 해당 주문을 DB에 저장하도록 구현하였습니다.
이 때, 주문 저장 로직이 추가되면서 해당 메서드를 트랜잭션으로 묶어야겠다고 생각했습니다.

주문 정보 저장이 실패하였지만, 상품 옵션 수량이 차감되는 문제가 발생할 수 있어 같은 트랜잭션에서 수행되도록 하는 것이 좋겠다고 생각했습니다.

그런데 이 경우에는 카카오 메시지 전송에 실패하여 주문이 생성되지 않는 경우도 존재하는데

Q1. 이런 경우에는 이 부분만 트랜잭션에서 제외시켜줄 수 있나요? 그러지 못한다면 일종의 트레이드 오프라고 생각해야할까요?

Q2. 만약 트랜잭션을 붙이지 않은 경우

코드의 실행 순서 상 상품 옵션 수량 차감이 먼저 수행되기 때문에 주문 정보를 저장에 성공한 경우에는 100% 상품 옵션 수량이 성공적으로 차감되었다고 보장할 수 있나요?

한 트랜잭션 안에서 실행되지 않은 작업들은 실행 순서를 신중하게 고려해야하나요?

}
}

public String getContentDescription() {

Choose a reason for hiding this comment

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

혹시 이게 어떤말이죠?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants