안녕하세요 강의 잘 듣고있습니다. MSA 프로젝트 요구사항 및 설계 부분에서 모놀로식으로 설계하게 될때는 FK 를 걸지만 MSA 에서는 DB가 독립적으로 분리되어 있어서 FK 를 걸 수 없을것 같습니다 . Application 단에서도 하지않나요 ?? 예를들어서 @ManyToOne user: User;
안녕하세요! 강의 들으면서 poll_consumer를 1개 > 2개로 늘려보았습니다 실습을 해보니 강의 화면에서 보여지는 것 처럼 consumer가 모든 파티션을 읽지않더라구요 (consumer 개수를 1 ~ 3개까지 변경해보아도 모든 파티션을 읽지 않습니다) 컨슈머가 모든 파티션을 읽지 않는 이유가 있을까요?
안녕하세요~ 람다 아키텍처에서 speed layer 부분에 궁금한 점이 생겨 질문드립니다! Q1. speed layer는 어딘가에 저장이 된 후에 serving layer에서 보여지는건지 궁금합니다 저장이 된 결과가 보여지는 거라면 serving layer에서 전 날의 결과도 같이 보여지게 되는걸까요? 아니면 별도의 설정 같은 걸로 전 날 데이터는 삭제가 되는걸까요? Q2. speed layer에서 보여진 데이터가 다음 날에는 batch layer에 들어갈텐데 데이터 중복은 어떻게 처리할 수 있나요? 감사합니다!
안녕하세요. 게시판 관련 강의를 듣는 도중 궁금한 점이 있어 질문을 남깁니다. 강의에서는 Snowflake 기반 단순 정렬을 기준으로 커버링 인덱스를 설명해주셨는데요, 실무에서는 여러 정렬 조건과 필터, 검색 조건이 함께 붙는 경우가 많습니다. ✅ 이런 복합적인 조회 패턴에서도 커버링 인덱스를 중심으로 설계하는 것이 적절한지, 아니면 다른 전략을 어떻게 사용해야 하는지 궁금합니다.(설명이 길어질 경우 간단한 힌트라도 부탁드립니다.) 💥 걱정하는 부분 정렬되는 컬럼 전부에 대해 인덱스를 걸면 더 문제가 발생할 것 같아요. 검색 %검색어% 의 경우에는 결국엔 full_scan 이어서 성능 최적화가 불가능하다.
Debezium이 데이터베이스 트랜잭션 로그(binlog, WAL 등)를 읽어서 변경사항을 Kafka로 발행한다는 것은 이해했습니다. 그런데 혼란스러운 부분이.... 일반적인 CRUD API 요청도 결국 DB에 변경을 가하는데, Debezium이 이를 어떻게 구분하는지 궁금합니다. 예를 들어 주문 생성 API → DB INSERT → 이건 CDC 이벤트로 발행해야 함 사용자 세션 저장 API → DB INSERT → 이건 CDC 불필요 이런 경우에는 어떻게 구분되나요?
강사님, 안녕하세요. 강의 잘듣고 있습니다! 실습을 하고 있는데 궁금한 점이 있어서 글을 남깁니다. 혹시 파티션 중 리더 replica를 선출하는 규칙이 있는지 궁금합니다. 예를들면 파티션에 1,2,3 번 replica가 있었고 리더였던 1번 replica가 중단됐을 경우 2번, 3번 replica 중 어떤 것이 리더가 되는지 궁금합니다. 시간되실때 답변 꼭 부탁드립니다. 감사합니다!
아키텍처 설계 부분에서 궁금한 점이 있어 질문드립니다. 일반적으로 원천 → 수집 → 저장 → 처리 → 제공 → 활용 의 흐름으로 데이터 아키텍처를 설명해주셨는데, 이 중 원천 에 해당하는 부분에서 조금 헷갈리는 지점이 있습니다. 이전 강의에서 DW, DL, Lakehouse를 구축할 때 원천 데이터를 가져와 별도의 저장소에 다시 저장하며, 이 과정에서 데이터가 중복될 수 있고 그 중복이 필요한 이유에 대해서도 설명해주셨던 것으로 이해했습니다. 그래서 제가 이해한 내용을 예시로 확인하고 싶습니다. 예를 들어, 사내에 어떤 제품이 있고 해당 제품의 백엔드 서버가 RDB를 사용하며 서비스 데이터가 그 RDB에 저장되고 있다고 가정했을 때, DW, DL, Lakehouse를 구축하는 관점에서는 이 서비스용 RDB 자체가 raw data source(원천 데이터) 가 되고, 이 RDB로부터 데이터를 수집하여 DW, DL, Lakehouse 등에 다시 적재하는 구조로 이해했는데 이렇게 이해하는 것이 맞을지 궁금합니다.
학습 관련 질문을 최대한 상세히 남겨주세요! 고민 과정도 같이 나열해주셔도 좋습니다. 먼저 유사한 질문이 있었는지 검색해보세요. 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 좋아요에 대한 정합성을 (article_id, user_id) 유니크 인덱스로 보장하는 것은 DB의 역할이라고 생각합니다. 그렇다면, 다수의 동시 요청이나 사용자의 반복 클릭(예: 좋아요 버튼을 연속으로 누르는 경우) 상황에서 불필요한 DB 부하와 예외 발생을 줄이기 위해 애플리케이션 단에서는 어떤 방식으로 이를 보완하고 처리하는 것이 적절한가요? 좋아요에 대한 정합성을 (article_id, user_id) 유니크 인덱스로 보장하는 것은 DB의 역할이라고 생각합니다. 다만, 다수의 동시 요청이나 사용자의 반복 클릭(예: 좋아요 버튼을 연속으로 누르는 경우) 상황에서는 애플리케이션 단의 단순한 선행 검증만으로는 이를 제어하기 어렵다고 느꼈습니다. 예를 들어, 아래와 같은 코드에서는 다음과 같은 경쟁 상태(race condition)가 발생할 수 있습니다. T1: exists → false T2: exists → false T1: insert T2: insert ❌ (유니크 제약 위반) if (!likeRepository.exists(postId, userId)) { likeRepository.save(...); } 이처럼 애플리케이션 레벨의 exists → insert 패턴이 동시성 문제를 해결하지 못하는 상황에서 DB 예외에만 의존하지 않고 불필요한 중복 요청과 예외 발생을 줄이기 위해 애플리케이션 단에서는 어떤 방식으로 이를 보완하는 것이 바람직하다고 보시는지 궁금합니다.
api gateway 에서 jwt 검증을 해서 분기를 시키는데, 세션-쿠키 방식일때도 api-gateway에서 처리 하나요 ? 만약 세션-쿠키 방식일 때,user-service 에서 인증 처리한다고 하면, order 서비스에서 인증이 필요한 경우, user-service로 http 통신으로 인증을 확인하나요 ?
강의에서는 비관적 락과 낙관적 락을 다루셨는데, 일반적으로 대규모 서비스가 아닌이상 좋아요 자체가 순식간에 많은 트래픽이 몰릴것같지않아 낙관락으로 처리하는 것이 더 효율적일것같다고 생각이듭니다. 그래도 극단적인 상황을 대비해서, 뒤에서 나오는 조회수 처리처럼 레디스로 좋아요 수를 증가시키고 스케줄링같은걸로 RDB에 백업하는 방식은 어떤가요? 동시성처리에서 비관적 락으로만 처리해야 하는 상황이 있을까요? 레디스의 분산 락을 사용하는 것이 성능 측면에서 비관락보다 유리할 때도 있을 것 같은데, 실제로 비관락을 반드시 써야 하는 예시나 사례가 궁금합니다. RDB 트랜잭션( @Transactional ) 내부에서 레디스를 함께 업데이트하는 경우, RDB에서 장애가 발생해서 롤백이됬는데 Redis 만 데이터가 업데이트 되는 경우도 발생할수도 있을것같은데. 이런 경우를 어떻게 처리하는지, 2PC를 적용하는지 아니면 다른 방법이 있는지도 궁금합니다.
현상: 아래에도 동일하게 이슈 올려주신 분이 있는 것으로 보이는데 postman으로 다음과 같이 요청하였을 때, kafka에서 DLT 소비하는 과정에서 무한 반복하는 현상이 발생하면서 DLT 토픽 자체에 계속 쌓이게 됩니다! @RetryableTopic에 autoStartDltHandler = False 설정을 해주거나, @DLTHandler를 정의 해줘야 DLT 소비를 무한으로 하지 않는 것 같은데 뭔가 설정이 잘못 된게 있을까요? ㅠ 요청 파라미터: { "from": "sender@naver.com", "to": "fail@naver.com", "subject": "heyyyy", "body": "helloo" } 2026-02-01T15:53:25.480+09:00 ERROR 15124 --- [email-send-consumer] [ner#0.dlt-0-C-1] k.r.DeadLetterPublishingRecovererFactory : Record: topic = email.send.dlt, partition = 0, offset = 605, 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:3014) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2914) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:2880) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:2790) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:2632) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:2526) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:2164) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeIfHaveRecords(KafkaMessageListenerContainer.java:1541) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1479) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1348) ~[spring-kafka-4.0.2.jar:4.0.2] 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-02-01T06:53:25.480044400Z at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:100) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:49) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2901) ~[spring-kafka-4.0.2.jar:4.0.2] ... 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.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:496) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invoke(MessagingMessageListenerAdapter.java:425) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:92) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:52) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.invokeDelegateOnMessage(KafkaBackoffAwareMessageListenerAdapter.java:106) ~[spring-kafka-4.0.2.jar:4.0.2] at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:97) ~[spring-kafka-4.0.2.jar:4.0.2] ... 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 Caused by: org.springframework.kafka.listener.TimestampedException: Exception thrown at 2026-02-01T06:53:25.480044400Z Caused by: org.springframework.kafka.listener.ListenerExecutionFailedException: invokeHandler Failed Caused by: java.lang.IllegalStateException: No Acknowledgment available as an argument, the listener container must have a MANUAL AckMode to populate the Acknowledgment. > Task :com.example.emailsendconsumer.EmailSendConsumerApplication.main() FAILED Execution failed for task ':com.example.emailsendconsumer.EmailSendConsumerApplication.main()'. > Build cancelled while executing task ':com.example.emailsendconsumer.EmailSendConsumerApplication.main()' * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights from a Build Scan (powered by Develocity). > Get more help at https://help.gradle.org. BUILD FAILED in 17s 3 actionable tasks: 2 executed, 1 up-to-date 코드 발생: 오류 발생하는 것으로 보이는 consumer 코드는 다음과 같습니다. 우선 강사님 코드와 다른 점은 Backoff 부분인데 이건 라이브러리 버전 문제 인 것 같아서 넘어갔습니다. @Service public class EmailSendConsumer { @KafkaListener( topics = "email.send", groupId = "email-send-group" ) @RetryableTopic( attempts = "5", backOff = @org.springframework.kafka.annotation.BackOff( delay = 1000, multiplier = 2.0 ), dltTopicSuffix = ".dlt" ) public void consume(String message) { System.out.println("Received message: " + message); EmailSendMessage emailSendMessage = EmailSendMessage.fromJson(message); if (emailSendMessage.getTo().equals("fail@naver.com")) { // 잘못된 이메일 주소로 인해 발송 실패 throw new RuntimeException("Simulated email sending failure"); } // 이메일 전송 로직 구현 생략 (예: SMTP 서버를 통해 이메일 전송) try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException("Email sending interrupted", e); } System.out.println("Email sent to: " + emailSendMessage.getTo()); } }
좋은강의 너무 감사드리며 많이 배워가는것같습니다. 강의를 들으면서 질문이 있어 드립니다. 포트 번호로 분리된 MSA 서버를 프론트엔드에서 쉽게 사용하도록 통합하는 방법이 궁금합니다. 일단 단순히 생각했었을때 ngnix 로 리버스프록시 적용해서 /article 이면 localhost:9000 이런식으로 리버스프록시로 구성해도 될것같은데 현업에서 nginx 를 이용해서 구성을 하는지 또다른 많이 쓰는 방법이 있는지 궁금합니다. CQRS 환경에서 서버끼리 http 통신 article-read 에서 cqrs 를 적용하여 각 서비스에서 필요한 정보를 http 통신으로 가져오는데 클라이언트와 서버가 아닌 서버와 서버끼리면 굳이 웹과관련된 정보가 전송되는 http가 비효율적(?)이지 않나? 라고 생각을했는데 데이터 통신을 할때 카프카로 필요한 데이터만 통신하는것은 어떤가요? 실제 msa 환경에서 서버끼리 통신할때 어떤방식으로 사용하는지도 궁금합니다.
학습 관련 질문을 최대한 상세히 남겨주세요! 고민 과정도 같이 나열해주셔도 좋습니다. 먼저 유사한 질문이 있었는지 검색해보세요. 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 무한스크롤을 공부하면서 쿼리플랜을 자세히 살펴보았습니다. rows라는게 예상 스캔 행 수 라고 찾게 되었는데요, offset을 사용하지않고 마지막 last_article_id로 limit 30개 해서 30개만 스캔하면 될줄 알았는데, 저는 6백만개가 나오고 강사님은 5522497개 (무한 스크롤 설계 - 6:31초)가 나오는걸 알게 되었어요. 이게 시간은 빠르긴한데 뭔가 스캔하는게 많아서 이상하다 싶었어요.. 그래서 인덱스를 지우고, (article_id desc, board_id)로 위치도 바꾸거나, use index로 인덱스를 강제하게 해봤는데 결과가 똑같더라구요.. 이 row를 줄이기 위한 방법이 있는지 궁금합니다. cladue에서는 최악의 경우의 row를 보여준다고 하면서도, 또다른 답변으로는 쿼리 수정으로 최적화해야한다 나뉘더라구요. 최악의 경우라 해도 왜 6백만개나 scan하는지.. 그냥 속도빠르니깐 넘어가도 상관없는지 의심이 들어서 더 찾아볼수있는 방법이나 조언 부탁드립니다. 감사합니다. 강의 거의 완강햇다가 한번 더 다시 듣고있습니다.
안녕하세요. 항상 유익한 강의를 제공해 주셔서 감사드립니다. 입문 강의들을 제외한 모든 강의를 수강하며 많은 도움을 받고 있습니다. 다름이 아니라, 개인적으로 Kafka 관련 내용을 공부하던 중 KRaft 모드에 관한 내용이 공식 문서 및 여러 자료에서 업데이트되고 있는 것을 확인하게 되었습니다. KRaft는 Kafka의 아키텍처에서 중요한 변화를 가져온 만큼, 관련 내용을 강의나 추가 자료로 공유해 주신다면 수강생들에게 큰 도움이 될 것 같아 노티 드립니다. 항상 좋은 강의 제공해 주셔서 감사하며, 앞으로도 많은 배움을 기대하겠습니다.