섹션 19 부분은 예전에 강의하셨을 때 쓰신 스프링부트 2점대 버전을 3점대 버전으로 업그레이드하는 강의인가요? 최신 강의 기준 업데이트 하신 3점대 버전으로 강의를 듣고 있는데 섹션 19에서 강의가 정확히 어떤 강의인지 궁금합니다. 만약 2점대 버전을 3점대 버전으로 업그레이드 하는 강의라면, 섹션 19에서 분산 트레이싱, swagger, kubernates 부분만 들어도 되는걸까요?
안녕하세요 선생님, 강의 복습 중에 조금 헷갈리는 부분이 생겨서 문의드립니다. 현재 github 에서 제공해주시는 SUBSMANAER 프로젝트에서 yaml/01-basic/ingress/sm-ingress-domain.yaml 파일에 보시면 다음과 같습니다. apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: sm-ingress namespace: subsmanager spec: ingressClassName: nginx rules: - host: subs-manager.com http: paths: - path: / pathType: Prefix backend: service: name: sm-frontend port: number: 80 - path: /users pathType: Prefix backend: service: name: sm-user port: number: 80 - path: /sub pathType: Prefix backend: service: name: sm-subs port: number: 80 - path: /recommend pathType: Prefix backend: service: name: sm-reco port: number: 80 그리고 sm-frontend/nginx.conf 파일을 보면 아래와 같이 설정되어 있습니다. server { listen 80; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } # API 요청을 백엔드 서버로 프록시 location /users { proxy_pass http://sm-user:5000; } location /sub { proxy_pass http://sm-subs:5000; } location /recommend { proxy_pass http://sm-reco:5000; } } 여기서 갑자기 헷갈립니다. 애초에 ingress 단에서 /sub/ /recommend , /user 요청에 대한 처리를 중간에 가로채서 처리를 하고 있는데, 굳이 frontend 에서 사용하는 nginx.conf 에도 설정하는 것이 좀 와닿지 않습니다. 이렇게 중복으로 설정해야되는 이유가 뭔지 궁금합니다!
안녕하세요 msa에서 db를 각자 쓰게되는 패턴이라면 join이 깨지지 않나 하는 생각이 들었습니다. 혹시 join을 하다가 db가 나뉘게되어 쿼리 효율이 떨어지는 경우도 있을까요? 아니면 애초에 join이 안쓰이기 때문에 마이크로서비스로 분리가될까요 예를들어 사용통계같은걸 볼 땐 모노리스에서 msa 전환 전에 join이 들어갈 수 있을 것 같은데, db가 나뉘어버리면 join을 못쓰는거죠? 그럼 msa 전환해야한다면 그때가 데이터레이크를 만들 타이밍인걸까요?
실습 중에 위와 같이 앞에 이상한 문자가 추가되는데 혹시 해결법을 아실까요? 붙여 넣기 하지 않고 직접 타이핑하고 있는데도 이상한 문자가 앞에 삽입이 되는 것 같습니다. 윈도우 환경에서 git bash 사용하고 있습니다. 안녕하세요. 황현우입니다. 강의에 포함된 내용이 아니더라도 강의를 듣다가 궁금하신 점이 생기시면 부담없이 편하게 질문해주세요. 실습에 오류가 발생했거나 명령어가 예상한대로 실행되지 않으신 경우 사용하시는 OS 환경과 화면을 캡처해서 보내주시면 빠르고 자세하게 답변을 드릴 수 있습니다. 질문은 가능한 빠르게 답변 드리겠습니다. 😀 감사합니다.
안녕하세요, 미래에 네이버에 취업할 취업준비생입니다. 다름 아니라, 이번 미니 프로젝트로 msa 환경을 바탕으로 하는 프로젝트를 기획중에 있습니다. msa에 대해 무지해서, 어떤 것부터 차례대로 강의를 수강하면 좋을지 말씀을 듣고싶어서 질문 남깁니다. 저는 웹 프로젝트를 준비 중에 있고, 언어로는 java를 사용, spring boot를 이용해서 개발하려고 합니다. 추가적으로 결제시스템까지 도입예정에 있어서, 후에 kafka 도입까지 고려하고 있습니다. 감사합니다.
POSTMAN에 to: fail@naver.com 으로 변경하여 send할 시 총 5번 재시도 후 강사님 처럼 멈추지 않고 아래 에러가 무한 반복이 됩니다. 2026-01-04T20:45:42.814+09:00 ERROR 20160 --- [ner#0.dlt-0-C-1] k.r.DeadLetterPublishingRecovererFactory : Record: topic = email.send.dlt, partition = 0, offset = 2, main topic = email.send threw an error at topic email.send.dlt and won't be retried. Sending to DLT with name email.send.dlt. org.springframework.kafka.listener.ListenerExecutionFailedException: Listener failed at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.decorateException(KafkaMessageListenerContainer.java:3011) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2911) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:2877) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:2787) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:2629) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:2523) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:2161) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeIfHaveRecords(KafkaMessageListenerContainer.java:1538) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1476) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ ListenerConsumer.run (KafkaMessageListenerContainer.java:1345) ~[spring-kafka-4.0.1.jar:4.0.1] at java.base/java.util.concurrent.CompletableFuture$ AsyncRun.run (CompletableFuture.java:1804) ~[na:na] at java.base/ java.lang.Thread.run (Thread.java:1583) ~[na:na] Caused by: org.springframework.kafka.listener.TimestampedException: Exception thrown at 2026-01-04T11:45:42.814281400Z at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:100) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:49) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2898) ~[spring-kafka-4.0.1.jar:4.0.1] ... 10 common frames omitted Caused by: org.springframework.kafka.listener.ListenerExecutionFailedException: invokeHandler Failed at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.checkAckArg(MessagingMessageListenerAdapter.java:519) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:496) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invoke(MessagingMessageListenerAdapter.java:425) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:92) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:52) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.invokeDelegateOnMessage(KafkaBackoffAwareMessageListenerAdapter.java:106) ~[spring-kafka-4.0.1.jar:4.0.1] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:97) ~[spring-kafka-4.0.1.jar:4.0.1] ... 12 common frames omitted Caused by: java.lang.IllegalStateException: No Acknowledgment available as an argument, the listener container must have a MANUAL AckMode to populate the Acknowledgment. ... 19 common frames omitted 위
강의의 시퀀스 다이어그램을 보았을 때, 2가지 정도 질문 드립니다. 1) stateless 요청에서 상태값의 전달 관련 일반적으로 웹과 같이 stateless 한 서비스를 고려했을 때, 비동기 처리로 인해 주문서비스가 고객의 주문요청에 대해 (이후의 수행작업과 상관없이) 즉시 리턴하게 되는 것이 예상되는데요. (위 그림에서는 동기적인 flow 로 주문완료 응답을 end point 유저에게 전달하는 것으로 보이긴 합니다만) 주문 완료 페이지에서 주문한 상품 정보나 결제 수단의 상태값 전/후 를 보여주는건 어떤 식으로 동작하게 될까요? 가령, 실물 상품이 아닌 연속선상의 사이버 재화(ex: 네이버포인트 등)를 지급해야하는 입장에서 주문 이전의 재화 정보와 주문 요청 후(최종적으로 트랜잭션 완료된 후의) 증/감 재화 정보를 유저에게 보여주어야 할 수도 있는데, 뒷단의 비동기 작업(이벤트 subscribe)을 수행하는 스레드랑 최초 주문 요청을 받는 요청스레드가 다른 부분에서 고려해야할 것이 더 있을 것 같은데요. 얼핏 생각했을 때는, 사용자에게 즉시 내려주는 응답 페이지는 PENDING 에 해당하는 페이지 UI 로 (ex: 결제 진행중... 표시) 전달하고, 사용자 PENDING 페이지 (클라이언트)에서 몇초 단위의 주기적인 서버 요청 등으로 완료 이벤트를 조회 하도록 처리하여 완료페이지로 리다이렉션 처리되는 방식이 될까요? 이 때, 중간 상태값은 별도 캐시서버등에 저장을 해야 할 것이고요. (리다이렉션 되는 완료페이지와 PENDING 페이지의 state가 또 다르므로) 2) 메시지 브로커(카프카) 를 쓸 때 고려사항 주제랑 좀 벗어난 걸 수는 있는데요, 보통 메시지 발행의 정확성을 위해 CDC (Change Data Capture) 를 설정하는 것이 일반적인 방식으로 알고 있습니다. 다만, 현재 서비스중인 DB 의 설정이나 부하 이슈로 CDC 를 쓰지 못하는 경우에는 어떤 방식으로 처리가능할지가 궁금합니다.
안녕하세요, 현재 Window 11 환경에서 실습 을 진행하는데 실습 중 문제가 발생하여 문의드립니다. 49. 구독매니저 YAML 구조 강의의 02:09 때 kubectl apply -f . 명령어를 사용한 후에 계속해서 Pod 생성 시에 ImagePullBackOff, CrashLoopBackOff 가 발생합니다. 문제가 발생한 POD 는 다음과 같고, 문제를 일으키는 문제들은 공통적으로 아래와 같은 메시지를 갖고 있었습니다. (kubectl describe 사용)
안녕하세요, 강의 들으면서 실습을 따라하던 중에 흐름이 끊기는 부분이 생겨서 문의드립니다. 섹션6 > sm-subs 강의 에서 04:52 에 나오는 python test/api_test.py 를 성공시키려면 일단 sm-user container 를 실행시켜야 하는데, 해당 컨테이너를 run 하는 부분이 섹션6 > sm-user 강의에 없는 거 같습니다. 일단은 아래처럼 명령어를 실행해서 진행하고 있는데 맞을까요? cd sm-user docker build -t sm-user . docker run -d -p 5000:5000 --name=sm-user --network=sm-network -e DB_HOST=sm-db sm-user
안녕하세요, 실습을 진행하던 중 문제가 발생하여 문의드립니다. 미리 말씀드리지만 저는 현재 Window 11 환경에서 실습을 진행중입니다. "서비스와 DNS" 목차에서 03:57 즈음에 알져주시는 명령어를 그대로 입력해봤습니다. 사용 명령어: kubectl run -it --rm --image=devwikirepo/ipbot debug -n hr -- /bin/bash 그랬더니 아래와 같은 에러가 발생했습니다. pod 생성 중에 문제가 생긴거 같아서 kubectl describe 명령어로 상태를 조회해봤습니다. ToastBread → kubectl describe pod debug -n hr Name: debug Namespace: hr Priority: 0 Service Account: default Node: easykube-worker3/172.22.0.3 Start Time: Wed, 31 Dec 2025 14:57:42 +0900 Labels: run=debug Annotations: <none> Status: Pending IP: 10.244.3.4 IPs: IP: 10.244.3.4 Containers: debug: Container ID: Image: devwikirepo/ipbot Image ID: Port: <none> Host Port: <none> Args: /bin/bash State: Waiting Reason: ImagePullBackOff Ready: False Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pd54v (ro) Conditions: Type Status PodReadyToStartContainers True Initialized True Ready False ContainersReady False PodScheduled True Volumes: kube-api-access-pd54v: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt Optional: false DownwardAPI: true QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 2m2s default-scheduler Successfully assigned hr/debug to easykube-worker3 Normal BackOff 24s (x5 over 116s) kubelet Back-off pulling image "devwikirepo/ipbot" Warning Failed 24s (x5 over 116s) kubelet Error: ImagePullBackOff Normal Pulling 6s (x4 over 2m2s) kubelet Pulling image "devwikirepo/ipbot" Warning Failed 3s (x4 over 116s) kubelet Failed to pull image "devwikirepo/ipbot": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/devwikirepo/ipbot:latest": no match for platform in manifest: not found Warning Failed 3s (x4 over 116s) kubelet Error: ErrImagePull 맨끝에 Events 를 보니 ImagePullBackOff 보고 처음에는 이미지 이름의 철자를 틀렸나 생각했지만, 해당 이름을 그대로 복사해서 docker hub 에서 검색해보니 잘나왔습니다. 좀 더 알아보니 no match for platform in manifest 이라는 문구가 눈에 들어와서 이게 CPU 아키텍처가 호환이 안되는 걸 알아냈습니다. linux/amd64 Window 환경에서도 실행할 수 있는 AMD64 도 지원해주실 수 있을까요??
TCC 구현하기(10) - Retry 를 활용하여 일시적인 오류에 대처하기 에서 spring retry를 사용하여 재시도를 적용하는 부분이 있습니다. 이에 의존성 추가 @EnableRetry 어노테이션 추가 아래 코드 추가 @Retryable( retryFor = { Exception.class }, noRetryFor = { HttpClientErrorException.BadRequest.class, HttpClientErrorException.NotFound.class }, maxAttempts = 3, backoff = @Backoff(delay = 500) ) 를 했었습니다. 하지만 스프링 프레임워크 7.0, 스프링 부트 4.0 버전부터는 spring retry가 spring core 패키지로 편입되어 아래와 같이 retry를 적용해야 합니다. 별도 의존성 추가 필요없음 @EnableRetry 어노테이션 대신 @EnableResilientMethods 어노테이션 추가 아래 코드 추가 @Retryable( includes = Exception.class, excludes = { HttpClientErrorException.BadRequest.class, HttpClientErrorException.NotFound.class }, maxRetries = 3, delay = 500 ) 위와 같은 방식으로 적용하시면 됩니다. 자주 쓰던 spring retry가 왜 안되나 싶어서 이유를 한참 찾았네요 허헛 참고 https://docs.spring.io/spring/reference/7.0-SNAPSHOT/core/resilience.html
안녕하십니까 상용님. MSA에 대한 학습이 처음이라 정말 흥미진진하게 잘 보고 있습니다. 다름이 아니라 TCC 구현하기의 마지막 수업인 11화에서 주문 도중 문제 발생 시 다음과 같이 일정 시간 동안 RESERVED 상태인 product들을 스케줄러가 따로 처리한다고 하셨는데 여기서 말하신 product는 product_reservation 엔티티를 말하신 것인지 궁금합니다. 감사합니다.
안녕하십니까 상용님. 수업 잘 듣고 있습니다. 다름이 아니라 TCC에서 PointService의 tryReserve 구현 시 Point 클래스에 reserve 메서드를 정의 하셨는데 이때 public void reserve(Long reserveAmount) { long reservableAmount = this.amount - reserveAmount; if (reservableAmount < reserveAmount) { throw new RuntimeException("금액이 부족합니다."); } reservedAmount+=reserveAmount; } 이렇게 reservableAmount가 reserveAmount를 빼는 것이 아니라 public void reserve(Long reserveAmount) { long reservableAmount = this.amount - this.reservedAmount; if (reservableAmount < reserveAmount) { throw new RuntimeException("금액이 부족합니다."); } reservedAmount+=reserveAmount; } 이렇게 reservedAmount를 빼주어야 하지 않나요? 감사합니다.