강사님이 추구하시는 생각하는 공부에 대해서 많이 고민해보게 되었습니다. 그렇다면 강사님이 생각하셨을 때, 이 강의를 보고 공부하는 이상적인 방법은 어떤게 있다고 생각하시나요? 예를 들면, 하나의 섹션을 먼저 다 보고 요구 사항 정리부터 다시 시작해보기 아니면 각 강의마다 끝나고 요구사항을 정리해보고 다음 넘어가기.. 등등 강사님도 커리큘럼을 만드실 때 이런식으로 하면 좋을 것 같다가 있으셨을 것 같은데 궁금합니다.
안녕하세요. 결제 부분 강의를 보니 payments API를 보면 컨트롤러에서 주문을 조회하고, 사용 할 쿠폰을 조회하고, 포인트를 조회하고, 조회 된 데이터를 PaymentService로 전달하는 스타일이더라구요. 제가 진행중인 사이드 프로젝트도 커머스가 주제입니다. 제 프로젝트도 처음에는 강의 코드 스타일대로 컨트롤러에서 필요한 데이터를 조합하고, 결제를 처리하는 Service 쪽으로 넘기는 형식이었는데, 이게 점점 결제 기능이 고도화되면서 뭔가 컨트롤러에서 비즈니스 로직의 흐름이 보이는게 맞나? 라는 생각이 들게 되었고 어느 순간부터 웬만한 Controller에서는 1개의 xxxService.method()만 호출하고 이 method가 요청에 대한 비즈니스 로직을 전부 담당하게 되었습니다. @Service class QuestionPaymentService( private val questionOrderGenerator: QuestionOrderGenerator, private val promotionApplier: PromotionApplier, private val orderCouponApplier: OrderCouponApplier, private val paymentCouponApplier: PaymentCouponApplier, private val questionPaymentRecorder: QuestionPaymentRecorder, private val pointCommandAPI: PointCommandAPI, private val eventPublisher: EventPublisher, ) { @Transactional fun payment(command: QuestionPaymentCommand): QuestionPayment { val order = questionOrderGenerator.generateQuestionOrder(command.userId, command.questionIds) val questionPayment = QuestionPayment.create(command.userId, order) promotionApplier.apply(order) orderCouponApplier.apply(questionPayment, command) paymentCouponApplier.apply(questionPayment, command) pointCommandAPI.usePoint(questionPayment.userId, questionPayment.realAmount) questionPaymentRecorder.record(questionPayment) eventPublisher.publish(toEvent(questionPayment)) return questionPayment } } 위 코드는 제 프로젝트의 결제 부분인데요. 강의에서 말씀하신 것처럼 Service가 너무 많은 걸 알게되더라구요. (주문도 생성하고, 쿠폰도 적용하고, 프로모션도 적용하고...) 지금 이 글을 작성하다보니, 갑자기 제 코드가 못생겨보이네요 .. 강의 코드와 비슷한 방식으로 위 코드를 바꿔본다면, 컨트롤러에서는 orderService를 이용해서 주문을 생성하고, couponService, promotionService 등을 이용해서 전처리를 한 뒤 PaymentService을 이용해 실 결제 금액만큼 금액을 지불하도록 하는 로직과 결제 내역을 저장하는 로직만 있을 것 같아요. 반대로 제 프로젝트 방식대로 강의 코드의 payments API를 만들어본다면, Payment를 만들기 위해서 PaymentCreateService와 같은 곳에서, orderReader, ownedCouponReader, pointReader 등을 조합해서 Payment를 생성하는 방식이 될 것 같아요. 결국 Service가 적은 책임만 가지게 된다면, Controller 입장에서는 복잡한 요청을 처리하기 위해선 다양한 Service를 조합하게 되고 Controller가 비즈니스 로직의 흐름을 보여주는 형태가 될 수 있다고 생각이 드는데요. ( 사실 Controller가 비즈니스 로직의 흐름을 보여주면 안된다는 걸 어디서도 듣지 않았지만 뭔가 어색한 것 같아요. ) 물론 계속 말씀하시는것 처럼 정답은 없다는 것은 알지만, 그냥 단순히 재민님은 주로 많은 책임을 가지는 Service보다는 Controller에서 작은 단위의 Service로 조합해서 처리하는 것을 선호하시는지 궁금합니다. 재민님을 지속 성장 가능한 소프트웨어 포스팅으로 알게되었고, 유튜브에서도 많은 도움이 되었어요. 그렇게 얻은 다양한 인사이트들을 개인 프로젝트에도 적용해보면서 다양한 시도를 하고 있는데 마침 제 관심사인 커머스 주제로 강의가 나와서 정말 행복합니다.
만약 카프카를 사용하고 있고 처리 실패하는 경우 retry 큐로 보내서 재시도 하는 방식을 사용하고 있다고 가정하겠습니다. 만약 서버가 메시지를 받아서 처리하던 중에 리밸런싱이 발생했습니다. 메시지를 성공적으로 처리했고 커밋을 하려했지만 리밸런싱이 발생해 커밋을 하지 못했습니다. 그럼 그 메시지는 다른 컨슈머가 다시 받아서 중복으로 처리할 수 있을것 같은데 어떻게 방지를 할 수 있나요..? 찾아보니 인박스 패턴이라는것이 있던데 메시지를 받았을 때 상태를 저장하고 이후에 재시도를 해도 상태값이 있다면 패스하는 방식으로 이해를 했습니다. 하지만 위에 상황에서 첫 컨슘에서 메시지를 처리하고 있다가 리밸런싱이 발생했고 이후에 다시 처리할 때 상태값이 있어서 패스 했습니다. 하지만 이후에 첫 컨슘에서 처리중에 예외가 발생했다면 어떻게 처리를 해야할까요...?
안녕하세요! 강의 너무 잘 보고 있습니다! 강의를 보는 중 궁금한 부분이 생겨 질문드려봅니다! @GetMapping("/callback") fun callback( ... return ResponseEntity.status(HttpStatus.FOUND) .location(URI.create("https://localhost:3000")).build() 콜백 함수에 return을 이렇게 작성하셨는데요! 만약에 서블릿객체를 이용해서 쿠키를 담지 않고 아래와 같이 하는 방법은 어떻게 보시는지요...?? 같은 동작을 할 것으로 예상은되는데 보편적인 스타일이 궁금합니다 ㅋ.ㅋ; ``` return ResponseEntity .status(HttpStatus.FOUND) .header("Set-Cookie", "authToken=$token; HttpOnly; Path=/; Max-Age=${60 60 24 * 7}") .location(URI.create(" http://localhost:3000 ")) .build() ```
안녕하세요 강사님! 강의 잘 듣고 있습니다ㅎㅎ 강의상에서 코루틴을 다른 스레드에서 수행시키기 위한 방법으로 Dispatchers 클래스에 정의된 Default 이름의 CoroutineDispatcher를 코루틴 빌더의 인자로 넣어주셨는데요. CoroutineDispatcher 구현체인 Default , IO , Unconfined 는 각각 어떤 차이점이 있는지가 궁금합니다. 내부 코드를 봤을땐 스레드 관리 방식과 스케줄링 방식 등에 차이가 있는 것 같은데, 각각의 Dispatcher가 언제 사용되는지, 가장 주요한 차이점은 뭔지가 궁금해요!! 그리고 각 Dispatcher는 내부적으로 스레드풀을 가지고 있고, 작업이 넘어왔을 때 내부적으로 관리하는 스레드 풀에서 스레드에 코루틴을 할당하는 방식으로 동작하는걸까요? 맞다면, Default 나 IO Dispatcher의 스레드 개수나 graceful shutdown 설정 등을 커스텀할 수 있는지도 궁금합니다!
aop의 단점을 보완하기 위해 advice 패턴을 사용하셨는데, 매번 코드에 advice가 들어가는게 좀 번거로울 수도 있을거 같다는 생각이 들어서 질문드립니다..! aop의 단점을 극복하기 위해 사용한거지만,, 혹시 Advice를 aop처럼 분리시켜서 적용시키는 방법이 따로 또 있을까요
안녕하세요. Kotlin과 Java의 현업에서의 활용에 있어 궁금한 내용이 있어 질문드려요. 서버를 개발하실 때 Kotlin과 SpringBoot를 활용한다고 수업시간에 지나가면서 잠깐 말씀해주셨는데요. 혹시 Kotlin + SpringBoot의 프로젝트 작업을 진행하실 때 전혀 Java를 사용하지 않고 활용하시는지 아니면 Java와 Kotlin을 같이 사용하는 지 현업에서 실제 어떻게 활용하시는지 궁금합니다. 그리고 Gradle에서 implementation("org.apache.commons:commons-lang3:3.0") 와 같은 외부 라이브러리를 Kotlin + SpringBoot 프로젝트에서 사용할 때 Java로 가져와서 사용하는지 아니면 Kotlin으로 따로 제작된 코드를 사용하는지 궁금합니다.
실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
querydsl이 쿼리를 코드로 작성하여 컴파일 시점에 오류를 감지할 수 있는게 가장 큰 장점인데 Spring JPA와 혼합하여 사용할떄 그 외에 장점이 또 있을까요 레거시 쿼리는 이미 사용되고 있어 이를 전환하기 위해 먼가 더 장점이 필요할 것 같아서요 아니면 레거시는 두고 신규 추가되는 부분만 Querydsl를 도입하는 식으로 가면 될까요?
실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
fetch join 부분이 조금 어렵게 느껴져서, gpt에게 물어보며 공부했습니다. 강의에서 fetch join으로 나온 중복 데이터를 DISTINCT 키워드를 이용해서 제거해주시는 부분을 보았는데, gpt가 다음과 같이 설명해주는 것을 보았습니다. DISTINCT 키워드가 SQL과 JPA 양쪽에서 다르게 동작하기 때문에 완전한 중복 제거가 보장되진 않습니다. 이 말이 맞다면 현업에서는 이런 문제를 어떤 식으로 해결하는지 궁금합니다!
안녕하세요 ! 강의 잘 듣고 있습니다. 최근 OutBox 패턴을 사용해보려고 시도중입니다 ! CDC도구를 사용하지않고 어플리케이션에서 폴링을 구현한다고 하였을 때 폴링전용 어플리케이션을 따로 두어야 하는지 궁금합니다. ex) 주문서버에서 DB에 저장 -> 폴링 서버에서 DB에 저장된 이벤트를 폴링으로 조회 후 메시지 발행 이렇게 생각한 이유는 주문서버에 폴링을 구현한다면 스케일 아웃 상황에서 폴링 로직도 저절로 스케일아웃되기때문입니다. 여러개의 폴링 로직이 같이 돌아가면 데이터가 꼬일 수도 있지 않을까 합니다. 어플리케이션 폴링구조의 outbox 패턴 구현에 대한 정석이 있는지 궁금합니다.
실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
안녕하세요. 수업 내용과는 크게 관계가 없지만... 예외처리 관련 내용에 대한 질문을 드립니다. 스프링이 제공하는 표준 예외( IllegalArgumentException , IllegalStateException )와 비즈니스 로직을 표현하는 커스텀 예외( NotFoundUserException ) 사이에서 표준 예외를 사용하는 경우와 커스텀 예외를 사용하는 경우에 대한 기준을 알 수 있을까요? 감사합니다.