현 국내 SI업체 소프트웨어 엔지니어(SA,AA)
다수의 대규모 차세대 프로젝트 개발방법론 및 설계방법 리딩
명지대, 서울과학기술대 클라우드,SW과목 강의
SK C&C,한국정보기술연구원(kitri), LG전자 MSA과정 강의
멀티캠퍼스, 한국표준협회, 한국품질협회 MSA 과정 멘토링,스파로스아카데미 웹개발 과정 멘토링
전문분야 : 개발방법론, 소프트웨어 아키텍처/설계/개발 기법
22년 , 위키북스 "도메인주도로 시작하는 마이크로서비스 개발", 저자
국가평생교육진흥원, K-MOOC "Microservice 설계 및 구현 " 강좌 개발 및 강의
강의
로드맵
전체 1수강평
- EDA 기반 Microservice 구현 (with Hexagonal, DDD)
- Microservice 내부 아키텍처 와 EventStorming 설계
- EDA 기반 Microservice 구현 (with Hexagonal, DDD)
- 마이크로서비스 아키텍처 와 MSA 패턴 이해
- Microservice 내부 아키텍처 와 EventStorming 설계
게시글
질문&답변
다른 BC 또는 마이크로서비스 담당 정보를 어떻게 이용하나요?
강의자입니다.^ ^ AI가 이미 답변을 드렸네요. AI 가 말한바와 같이 API,이벤트기반 통신,데이터 복제로 해결할 수 있으며, 의외로 백엔드 서비스간의 연관관계보다는 프론트엔드 단의 정보를 가져갈 수도 있습니다.예를 들면 대여서비스가 필요한 도서정보와 사용자정보를 프론트에 이미 조회된 정보를 가져갈 수 있습니다. 물론 백엔드 대여 서비스에서 실제 사용자가 존재하는지? 와 도서가 대여가능한지에 대한 검증은 필요합니다.이후 강의를 더 듣다 보시면 더 깊은 이해가 가능하실 것이라 판단됩니다. 감사합니다.
- 0
- 3
- 25
질문&답변
VO 클래스의 불변 필드 선언을 하지 않으신 이유?
강의자 한정헌입니다.답변드리면 도메인 주도 설계에서 vo를 설계할 때 불변성을 유지하는 것이 매우 중요한 원칙 중 하나입니다. 따라서 private final로 필드를 선언하여 vo 내부 상태가 변경되지 않게 안전하게 함 좋은 설계 방법입니다. 하지만 이렇게 하면 내부에서 사용하는 객체,List,Map등이 있다면 이 또한 모두 불변으로 유지해야 하고 외부 객체 연동 시 불변성을 유지하는 코드 작성 등 객체 내부의 복잡성이 증가하고 추가 적인 처리 코드 작업이 필요합니다. 따라서 불변성을 유지하는 것의 중요성과 그것을 유지하기 위한 복잡성 추가 등을 트레이드 오프 할 필요성이 있습니다.본 강의에서는 이러한 점을 고려하여 ,특히 가독성 측면에서 final처리하지 않는 것이 좋다고 생각했습니다. 그리고 rentalItem 은 이전 모델링 강의 에서 VO 로 선언했으나 overDued의 값이 변경됨으로 불변성을 잃음으로서 엔티티로 다시 개념 정의 한 부분입니다. 그런데 엔티티임에도 @Entity 처리를 안 한점을 지적 주셨는데 JPA의 @Entity와 도메인 주도 설계(DDD, Domain-Driven Design)에서의 엔티티(Entity)는 서로 다른 맥락에서 사용되는 개념입니다. 두 개념 모두 "엔티티"라는 용어를 사용하지만, 목적과 초점이 다릅니다.DDD의 엔티티 상태와 동작 : 데이터뿐만 아니라 행위(메서드)를 포함식별성(식별자을 기준으로 동일성 판단) JPA의 엔티티데이터베이스와 동기화의 목적데이터베이스 테이블과 매핑됨을 의미반드시 기본키가 존재해야 함.(@id)물론 현실적으로 데이터베이스와 매핑시 JPA의 @Entity와 DDD의 엔티티는 함께 사용되는 경우가 많습니다. 그렇지만 반드시 그래야만 하는 것도 아닙니다.즉 비지니스 로직을 처리하기 위한 도메인 모델링 과 데이터 처리를 하기위한 or매핑과는 전혀 다른 영역이라 생각합니다.따라서 RentalItem의 경우 초기에 도메인 모델링 관점에서 행위가 없기 때문에 vo로 설계했으나 overdued등의 값이 변경되어 불변성이 깨진다 판단하여 엔티티로 개념을 변경한 것이지만 역시 vo의 성격이 강하기 때문에 vo처럼 처리 했다고 생각하시면 될 것 같습니다.
- 0
- 2
- 54
질문&답변
RentalCard 의 calculateLateFee 메서드 구현이 잘못된 것 같습니다.
tkwoo님 안녕하세요. 강의자입니다.네 지적하신 점이 맞습니다. 제가 코드의 결함을 인지하고 제공하는 소스코드에는 이후 변경하였는데, 동영상 상에는 공지를 제대로 하지 못했네요. 죄송합니다. 다시 확인해서 공지를 하도록 하겠습니다. 지적 감사합니다.
- 1
- 2
- 61
질문&답변
20분정도에 말씀 하신 내용 질문 있습니다.
움 아키텍처를 설명하기 위해 개념적인 용어들을 사용했지만 이런 기능은 카프카 매커니즘이 이미 제공하고 있습니다. 예를 들어 간단히 구현해 보면 다음과 같습니다. 주문 서비스가 새로운 주문을 생성하면, 이 주문 정보를 Kafka에 이벤트로 보내고, 주문 이력 서비스가 이 이벤트를 읽어 데이터베이스에 주문 이력을 저장한 후, 메시지가 성공적으로 처리되었음을 확인하고 Kafka에서 해당 메시지를 제거하는 방식으로 구현할 수 있습니다.아래는 이 과정을 간단한 샘플 코드와 함께 설명하겠습니다.1. Kafka에 주문 이벤트 보내기 (주문 서비스)주문 서비스가 주문 정보를 Kafka에 보내는 과정입니다. Python의 kafka-python 라이브러리를 사용하여 Kafka에 메시지를 보내는 예제입니다.from kafka import KafkaProducer import json # Kafka 프로듀서 설정 producer = KafkaProducer( bootstrap_servers='localhost:9092', value_serializer=lambda v: json.dumps(v).encode('utf-8') ) # 주문 데이터 order = { 'order_id': '12345', 'customer_id': '67890', 'amount': 100.0 } # Kafka 토픽에 주문 이벤트 전송 producer.send('order-topic', order) producer.flush()2. 주문 이력 저장 및 Kafka 메시지 삭제 (주문 이력 서비스)주문 이력 서비스가 Kafka에서 메시지를 읽어 데이터베이스에 저장하고, 저장이 완료되면 Kafka에서 해당 메시지를 제거합니다. Python의 kafka-python과 mysql-connector-python을 사용한 예제입니다.from kafka import KafkaConsumer, KafkaProducer import mysql.connector import json # Kafka 컨슈머 설정 consumer = KafkaConsumer( 'order-topic', bootstrap_servers='localhost:9092', auto_offset_reset='earliest', group_id='order-history-group', value_deserializer=lambda x: json.loads(x.decode('utf-8')) ) # Kafka 프로듀서 설정 producer = KafkaProducer( bootstrap_servers='localhost:9092', value_serializer=lambda v: json.dumps(v).encode('utf-8') ) # MySQL 데이터베이스 설정 db_conn = mysql.connector.connect( host='localhost', user='youruser', password='yourpassword', database='yourdatabase' ) cursor = db_conn.cursor() # Kafka 메시지 처리 for message in consumer: order = message.value order_id = order['order_id'] customer_id = order['customer_id'] amount = order['amount'] # 주문 이력을 데이터베이스에 저장 cursor.execute( "INSERT INTO OrderHistory (order_id, customer_id, amount) VALUES (%s, %s, %s)", (order_id, customer_id, amount) ) db_conn.commit() # 메시지 처리가 완료된 후 Kafka에서 메시지 삭제 (offset 커밋) consumer.commit() # 메시지 전송 상태를 확인하고 로그 기록 또는 다른 작업 수행 print(f"Order {order_id} processed and saved to database.") 코드 복사요약주문 서비스: 주문을 Kafka 토픽에 메시지로 전송합니다.주문 이력 서비스:Kafka에서 메시지를 읽어 데이터베이스에 주문 이력을 저장합니다.메시지를 처리한 후 Kafka의 오프셋을 커밋하여 메시지가 더 이상 처리되지 않도록 합니다.엄밀히 말하면 Kafka에서 오프셋(offset)은 특정 파티션 내에서 메시지의 위치를 나타냅니다. 즉 소비자가 메시지를 처리한 후, Kafka의 컨슈머 그룹은 해당 메시지가 성공적으로 처리되었다는 것을 나타내기 위해 오프셋을 커밋합니다. 오프셋 커밋은 Kafka가 메시지를 어느 지점까지 읽었는지를 기록합니다. 즉, 컨슈머가 다음에 재시작될 때, 마지막으로 커밋된 오프셋부터 메시지를 읽기 시작합니다. 따라서 한번 전달된 메시지가 다시 전달되지 않음을 보장합니다. 그리고 카프카의 메시지는 카프카 로그 보존기간에 따라 자동으로 삭제되게 하면 됩니다.
- 0
- 2
- 130
질문&답변
CQRS 6분정도 강의 질문드립니다.
안녕하세요. 강의자 입니다.여기서 이벤트 핸들러는 스프링 이벤트 핸드러 같은 구체적인 기술을 애길한 것은 아니고요. 개념적인 의미로 말씀 드린겁니다. cqrs가 명령( CUD)와 조회( R)를 분리하여 효율성을 높이는 패턴인데요. 예를 들면 주문 서비스가 커맨드 서비스 역할을 수행하고 주문이력 서비스가 조회서비스를 수행한다면, 각기 서비스가 분리되어 있기 때문에 주문이 입력되고, 주문이력이 입력되는 트랜젝션은 분리되기 때문에 분산 트랜젝션이 수행되어 합니다. 이 경우 2개 서비스의 정합성을 맞춰주기 위해 주문 서비스에서 주문 입력 트랜젝션 시 함께 비동기 이벤트를 발생시켜서 주문 이력 서비스에 주문이 들어왔다는 것을 알리는 것이죠. 여기서 주문 이력 서비스의 이벤트 핸들러는 이벤트가 들어왔다는 것을 알아채는 개념적인 기능를 설명하고 있는 겁니다. 여러 가지 기술로 구현 가능할 수 있죠. 주문이력서비스에서는 이벤트 핸들러를 통해 주문이 이벤트를 식별한 뒤 주문 이력 서비스에 주문 이력을 생성하면 주문서비스와 정합성 이 맞게 되는 거죠.
- 0
- 2
- 128
질문&답변
질문드립니다,
강의자 한정헌입니다. 답변 드리면 다음과 같습니다.먼저 개념 정의부터 명확히 하면, 노드 및 컨테이너: 노드는 컨테이너가 실행되는 물리적 또는 가상 머신입니다. Kubernetes 클러스터의 각 노드는 여러 컨테이너를 호스팅할 수 있습니다. 따라서 3개의 노드와 8개의 컨테이너가 있는 경우 해당 컨테이너는 Kubernetes 스케줄러의 결정에 따라 3개 노드에 걸쳐 예약됩니다. 스케줄링: Kubernetes 스케줄러는 노드에 컨테이너(Pod로 패키징됨)를 배치하는 일을 담당합니다. 이는 taints , tolerations, affinity 규칙과 같은 다양한 요소를 기반으로 수행됩니다. 목표는 로드 균형을 맞추고 사용 가능한 리소스를 효율적으로 사용하는 것입니다. 따라서 특정 제약 조건이나 선호도 규칙이 없다고 가정하면 8개의 컨테이너(각 컨테이너가 자체 포드에서 실행된다고 가정)가 3개 노드에 배포됩니다. 그렇지만 분포가 고르지 않을 수도 있습니다. 예를 들어노드 1에는 포드가 3개 ,노드 2에는 포드가 3개 , 노드 3에는 포드가 2개 있을 수 있습니다. 요약하면 Kubernetes는 기본적으로 컨테이너를 노드 전체에 균등하게 나누지 않습니다. 대신 리소스 가용성 및 기타 요소를 기반으로 컨테이너를 예약합니다. 배포는 사용자가 설정한 다양한 구성 및 제약 조건의 영향을 받을 수 있습니다. Spring Cloud API Gateway 앞에서 NGINX를 Ingress 컨트롤러로 사용하는 경우 말씀하신 바대로네트워크 지연 가능성이 있을 수 있으나 매우 경미할 것 같네요. 이를 이중 프록시 레이어로 구성한다고 하는데요. 이런 경우에는 다음과 같이 역할을 줄 수 있겠죠. NGINX 수신 컨트롤러: Kubernetes 클러스터로 들어오는 외부 트래픽을 처리합니다. SSL 종료, 호스트 이름 또는 경로 기반 라우팅과 같은 작업을 수행하고 로드 밸런싱도 처리할 수 있습니다.Spring Cloud API 게이트웨이: 클러스터 내에서 작동하며 라우팅, 로드 밸런싱은 물론 인증, 속도 제한 등과 같은 추가 기능을 제공합니다. 이런 경우 각 계층은 추가 처리 및 네트워크 홉으로 인해 약간의 오버헤드 및 잠재적으로 대기 시간이 증가할 수 있습니다. 그렇지만 이런 아키텍처 결정 전에 우선NGINX와 Spring Cloud API 게이트웨이가 모두 필요한지 고민할 필요가 있을 것 같네요. 또 이런 결정시에는 각 요소의 아래와 같은 장단점을 고려해야 할 것 같네요. NGINX: 클러스터 가장자리에서 트래픽 라우팅 및 로드 밸런싱을 처리하는 데 매우 효과적 Spring Cloud API Gateway: 동적 라우팅, 속도 제한, 서비스 간 통신과 같은 고급 기능을 제공. 네 더 많은 물리적 서버 노드를 추가하여 Kubernetes 클러스터를 확장하는 것은 용량과 복원력을 높이는 강력한 방법이 될 수 있습니다 , 그렇지만 k8s 와 같은 경우 수평형 Pod Autoscaling 를 우선 고려하고 이것이 한계에 도달할 경우 Cluster Autoscaler를 구현하여 단계적으로 Pod 및 노드 수를 동적으로 조정하는 것이 일반적인 방법입니다. 또 Pod Autoscaling 를 고려하여 더 크거나 더 강력한 노드를 추가하는 것이 작은 노드를 많이 추가하는 것보다 더 효과적일 수 있습니다. 당연히 private 망에서도 k8s를 구성할 수 있습니다.예 로컬에 k8s 구성하는 경우 다음과 같이 구성 가능합니다. 개발/테스트: 노드 1개(마스터 및 작업자 역할이 모두 포함된 단일 노드 설정) ex) Minikube기본 프로덕션: 마스터 노드 1개 + 작업자 노드 1개(고가용성 부족으로 인해 프로덕션에는 적합하지 않음)권장 프로덕션: 마스터 노드 3개 + 작업자 노드 2개(고가용성 및 내결함성을 위해)
- 0
- 1
- 104
질문&답변
레어어드 아케텍처 스타일 질문드립니다.
네 mvc모델에서 j2ee패턴의 dto 나 vo가 모델의 역할을 수행하는 거죠.즉 레이어드 아키텍처 스타일을 논하면서 mvc모델과 같은 개념으로 이해하는 분들이 많아서 언급한 내용이고요. 굳이 mvc모델을 레이어드 아키텍처와 매핑해보면 프리젠테이션 레이어의 역할과 매핑됩니다.그리고 두번째 질문에 답변을 드리면, 데이터 엑세스 계층은 제가 레이어드 아키텍처 예를 들며, 4개층 (프리젠테이션,비지니스,퍼시스턴스,데이터베이스) 이렇게 정의했는데 이건 개별 회사 및 시스템 마다 다를 수 있습니다. 용어도 다를 수 있고요. 어떤 프로젝트 에서는 (퍼블리싱,서비스,비지니스로직,데이터엑세스)이런식으로 구분하기도 합니다. 그렇지만 보통 데이터 엑세스 계증은 퍼스시턴스 계층과 같은 용어로 사용됩니다. 감사합니다.
- 0
- 1
- 116
질문&답변
UserCase가 많은데... 이유?
네 강의자 한정헌입니다.말씀하신 바대로 헥사고널 아키텍처에서도 예를 들면 CUD단위로 하나의 유스케이스 인터페이스로 묶고 이를 구현하는 입력포트도 하나의 클래스로 작성할 수 있습니다. 처음에 간단했던 cud야 괜찮겠지만 비지니스가 점점 복잡해지면 유지보수가 거듭되면서 이 클래스가 점점 비대해질 가능성이 있습니다. 그래서 저는 기능 단위로 유스케이스와 입력포트를 분리하는 방법을 선호합니다. 유스케이스 단위로 클래스 생성하는 방식에 대해서는 클린코드의 저자 로버트 c 마틴이 ‘ 소리치는 아키텍처(코드의 명칭을 통해 그 의도롤 소리치게 하자.)’라고도 언급하며 강조를 하기도 했습니다. 왜냐면 이렇게 했을 경우 코드명으로 그 의도를 바로 식별할 수 있기 때문이죠. 즉 클래스 명만 보고 어떠한 유스케이스인지 쉽게 인지 가능하기 때문에 테스트 및 유지보수성 높아 질수 있음을 강조했다고 생각합니다.감사합니다.
- 0
- 1
- 380
질문&답변
음성 부분이 너무 깨지는데...
강의자입니다. 죄송합니다.제가 음질을 개선 시킨다고 한것이 더 망쳐 놨네요.주변 잡음이 들린다 하셔서 잡음을 잡는 다는 것이 목소리가 너무 울리게 했네요.우선 목소리가 울리지 않고 잡음이 줄이는 방식으로 급히 조치를 했습니다.좀더 보완하고 개선하도록 노력하겠습니다.
- 0
- 1
- 290
질문&답변
CQRS 질문 드립니다.
네 CQRS의 개념과 장점에 대해 정리해 보면 다음과 같습니다.관심 사항 분리: CQRS는 데이터를 수정하는 작업(명령)과 데이터를 읽는 작업(쿼리) 간의 명확한 분리를 촉진합니다. 이러한 분리는 각 구성 요소를 특정 책임에 집중함으로써 애플리케이션의 설계 및 유지 관리를 단순화합니다.최적화: 명령과 쿼리를 분리하여 각 측면을 특정 요구 사항에 따라 독립적으로 최적화할 수 있습니다. 예를 들어, 명령 측은 쓰기가 많은 워크로드에 최적화되어 빠르고 일관된 데이터 업데이트를 보장할 수 있으며, 쿼리 측은 읽기가 많은 워크로드에 최적화되어 효율적인 데이터 검색 및 표시가 가능합니다.확장성: CQRS를 사용하면 해당 작업 부하에 따라 명령 및 쿼리 구성 요소를 독립적으로 확장할 수 있습니다. 이러한 유연성을 통해 리소스 활용도를 높이고 성능을 조정하여 다양한 수준의 트래픽과 처리 요구 사항을 처리할 수 있습니다.복잡한 도메인 처리: 데이터 모델이나 비즈니스 로직이 복잡한 복잡한 도메인에서 CQRS는 보다 표현력이 뛰어나고 유지 관리가 쉬운 솔루션을 제공할 수 있습니다. 이를 통해 개발자는 도메인의 특정 요구 사항에 맞게 명령 및 쿼리 모델을 맞춤화하여 두 가지 책임을 단일 모델에 맞추려고 할 때 발생할 수 있는 타협을 피할 수 있습니다.이벤트 소싱 통합: CQRS는 또 다른 아키텍처 패턴인 이벤트 소싱과 함께 사용되는 경우가 많습니다. 이벤트 소싱에는 애플리케이션 상태에 대한 모든 변경 사항을 일련의 불변 이벤트로 캡처하는 작업이 포함됩니다. CQRS는 이벤트를 트리거하는 명령과 이를 사용하는 쿼리를 명확하게 구분하고 이벤트 기반 아키텍처를 촉진하며 변경 사항을 감사하고 재생하기 위한 강력한 메커니즘을 제공함으로써 이벤트 소싱을 보완합니다.유연성: CQRS는 애플리케이션의 다양한 부분에 적합한 데이터 저장 메커니즘과 쿼리 모델을 선택할 수 있는 유연성을 제공합니다. 예를 들어, 명령 측에서는 트랜잭션에 최적화된 기존 관계형 데이터베이스를 사용할 수 있고, 쿼리 측에서는 읽기 작업이 많은 워크로드에 최적화된 NoSQL 데이터베이스 또는 복잡한 쿼리를 위한 특수 인덱싱 솔루션을 사용할 수 있습니다.질문하신 내용을 개념에 대비해서 살펴보면 1,4번에 해당되는 것처럼 도메인 모델이 복잡해지는 것을 피하기 위해 단순 질의를 분리하면 유지보수성과 유연성이 높아 질 수 있습니다.커맨드 와 질의 에 대한 서버분리를 질문하셨는데 2,3의 최적화와 확장성을 위해서는 각각의 서버(서비스)를 분리하는 것이 효과적이라고 생각합니다.그리고 로직의 검증을 위한 조회나, 로직상 필요한 조회는 명령의 부분이라고 생각합니다.감사합니다.
- 0
- 1
- 287