개요

이전 포스팅에서 MSA에 대해 간단히 알아보았다.

그럼 어떻게 MSA의 아키텍쳐를 구성하고 연결할 수 있을까?

도메인 주도 설계로 시작하는 마이크로 서비스 개발을 공부해보며 정리해보려한다.

JHipsterJava Hipster로 웹 애플리케이션과 마이크로서비스 아키텍쳐를 빠르게 적용, 개발, 배포할 수 있도록 도와주는 오픈소스 개발 플랫폼이다.

생성시 옵션으로 모노리스, 마이크로서비스, 게이트웨이를 선택할 수 있고 도커, JPA등 의 환경을 구축하고 바로 실행 가능한 애플리케이션을 만들어준다.또한 인증 처리 및 REST API를 이용한 통신을 지원한다.

JHipster는 설치가 간편하고 디렉터리를 생성한 후 몇가지 옵션만 선택하면 실행가능한 애플리케이션을 생성해준다.

환경 구축

  1. 자바 11 설치
  2. Node.js 설치
  3. JHipster 설치

시작하기

JHipster를 이용하여 내외부 아키텍처 빛 개발 환경을 구축할 수 있다. 여기서 생성되는 레지스트리는 스프링 유레카와 스프링 클라우드 컨피그를 사용하여 레지스트리 및 컨피그 서비스를 제공한다.

게이트웨이는 줄 기반이며 사용자 관리 및 로그인 기능을 제공하는 프론트엔드 서비스도 통합해서 제공한다.

microservices_architecture_detail 003

JHipster로 마이크로 서비스 애플리케이션을 개발하는 순서는 이렇다.

  1. 게이트웨이 생성
  2. 레지스트리 생성
  3. 마이크로 서비스 생성
  4. 생성한 마이크로 서비스에 엔티티 생성
  5. 생성된 엔티티를 게이트웨이가 인식할 수 있도록 게이트웨이에 등록

우선 게이트웨이와 레지스트리를 생성해보자.
JHipster
위의 링크에서 4번까지 따라하면 된다.

What is the base name of your application? gateway
? Which *type* of application would you like to create? Gateway application
? Do you want to generate a feign client? No
? Besides Junit, which testing frameworks would you like to use? 
? As you are running in a microservice architecture, on which port would like 
your server to run? It should be unique to avoid port conflicts. 8080
? What is your default Java package name? com.mycompany.myapp
? Which service discovery server do you want to use? JHipster Registry (legacy, 
uses Eureka, provides Spring Cloud Config support)
? Which *type* of authentication would you like to use? JWT authentication 
(stateless, with a token)
? Which *type* of database would you like to use? SQL (H2, PostgreSQL, MySQL, 
MariaDB, Oracle, MSSQL)
? Which *production* database would you like to use? MySQL
? Which *development* database would you like to use? H2 with in-memory 
persistence
? Which cache do you want to use? (Spring cache abstraction) Hazelcast 
(distributed cache, for multiple nodes, supports rate-limiting for gateway 
applications)
? Do you want to use Hibernate 2nd level cache? Yes
? Would you like to use Maven or Gradle for building the backend? Gradle
? Which other technologies would you like to use? 
WARNING! Non reactive gateway is not supported. Switching to reactive.
? Which *Framework* would you like to use for the client? React
? Do you want to enable *microfrontends*? No
? Besides Jest/Vitest, which testing frameworks would you like to use? 
? Do you want to generate the admin UI? Yes
? Would you like to use a Bootswatch theme (https://bootswatch.com/)? Default 
JHipster
? Would you like to enable internationalization support? Yes
? Please choose the native language of the application Korean
? Please choose additional languages to install 

위에서 원하는 옵션을 선택하고 엔터를 누르다보면 지정한 디렉터리에 프로젝트가 생성되는 것을 볼 수 있다.

스크린샷 2024-05-11 시간: 22 14 14

여기까지 성공하면 게이트웨이, 프론트엔드, 레지스트리가 모두 포함된 소스코드가 생성된 것이다.

이렇게 생성된 소스코드를 실행해보자.

레지스트리는 도커 컨테이너로 제공되기 때문에 도커 컴포즈로 실행하면 된다.

docker-compose -f src/main/docker/jhipster-registry.yml up 

실행하면 다음과 같이 로컬 주소가 출력된다.

gateway-jhipster-registry-1  | ----------------------------------------------------------
gateway-jhipster-registry-1  | 	Application 'jhipster-registry' is running! Access URLs:
gateway-jhipster-registry-1  | 	Local: 		http://localhost:8761/
gateway-jhipster-registry-1  | 	External: 	http://172.20.0.2:8761/
gateway-jhipster-registry-1  | 	Profile(s): 	[composite, dev, api-docs]
gateway-jhipster-registry-1  | ---------------------------------------------------------- 

접속해보면…

스크린샷 2024-05-11 시간: 22 19 24

이렇게 부트스트랩으로 만든 듯한 UI가 나온다.

기본 아이디와 비밀번호는 둘 다 admin이다.

로그인을 하면 다음과 같은 화면이 나온다. 스크린샷 2024-05-11 시간: 22 22 51

이제 게이트웨이를 실행해보자.

위에서 실행시킨 레지스트리를 종료하지 말고 새로운 터미널을 열어서 gradle이라면 ./gradlew 명령어를 통해 실행시킨다. 혹여나 인텔리제이나 IDE에서 직접 실행시키면 안된다.

성공적으로 실행이 되었다면 이렇게 접속 가능한 주소를 출력한다.

----------------------------------------------------------
        Application 'gateway' is running! Access URLs:
        Local:          http://localhost:8080/
        External:       http://127.0.0.1:8080/
        Profile(s):     [dev, api-docs]
----------------------------------------------------------

스크린샷 2024-05-11 시간: 23 29 37

우측 상단의 인증을 눌러서 레지스트리와 동일하게 admin으로 로그인한다.

이제 마이크로 서비스를 만들어볼 차례이다.

디렉터리를 만들고 처음에 게이트웨이를 생성했던 것처럼 똑같이 진행해준다.

포트번호와 패키지 설정을 다르게 해야하는 것에 유의한다.

게이트 웨이의 포트번호가 8080이니 8081, 8082… 정도로 설정해준다.

각 서비스에 맞게 설정을 해서 생성해줬으면 이제 각 서버를 실행시켜준다.
그리고 레지스트리 ip인 http://localhost:8761/에 접속해주면 이렇게 생성한 서비스들이 올라와있는 것을 볼 수 있다.

스크린샷 2024-05-12 시간: 21 29 10

여기까지 했으니 다음 단계는 엔티티를 생성하는 것이다.

엔티티를 생성하는 방법에는 2가지가 있다.

  1. 터미널에서 명령어를 통해서 생성하는 방법
  2. JHipster Online을 통해 생성하는 방법.

마이크로 서비스가 생성된 디렉토리 cd명령어를 통해 들어간다.

그 다음 아래의 명령어를 통해 엔티티를 생성한다.

jhipster entity 엔티티이름

이번에는 JHipster Online을 통해서 생성하는 방법을 알아보자.
JHipster Online
위의 링크에 들어가서 왼쪽 하단의 JDL Stdio를 클릭하여 마치 ERD를 짜듯이 작성해주면 된다.

그런 다음 오른쪽 상단의 다운받기 버튼을 클릭하면 jhipster-jdl.jdl파일이 다운받아진다. 이 것을 마이크로 서비스 디렉토리의 내부에 옮긴 뒤 jhipster import-jdl jhipster-jdl.jdl 명령어를 입력해준다.

그런 다음 이렇게 추가된 엔티티를 게이트웨이에 등록해준다.

cd 게이트웨이
jhipster entity 등록할 엔티티 이름

(만일 생성한 엔티티가 4개라면 jhipster entity 명령어를 4번 실행시켜 주는 것이다.)

이렇게 하고 백엔드 서비스를 다시 실행해준다.

도커 컴포즈 -> 게이트웨이 실행 -> 마이크로서비스 실행

여기까지 하고 게이트웨이 아이피로 접속하여 로그인한 뒤 오른쪽 상단의 entity를 선택하여 확인해본다.

JHipster에서 더미데이터를 생성해주고 CRUD작업이 가능한 상태로 제공해준다.

스크린샷 2024-05-12 시간: 22 14 51

이렇게 해서 MSA 아키텍쳐를 구성할 수 있다.

리펙토링

MSA 아키텍처에 맞게 서비스 내부의 아키텍처도 변경해주어야한다.

DTO, 매퍼, 카프카 등은 외부의 다른 서비스와의 통신에 사용된다. 이들은 세부 기술에 의존하는 외부 영역에 해당한다. 그러나 JHipster는 DTO와 매퍼를 service에 생성해주고 카프카는 web.rest에 생성한다.

그러나 헥사고날 아키텍처를 생각해보면 외부의 요청을 처리하는 인바운드 어댑터에 해당하는 REST컨트롤러에서 필요로하는 DTO, Mapper는 rest패키지에서 관리하는 것이 적절하고 자신의 서비스에서 다른 서비스를 호출하는 역할을 하는 카프카나 페인같은 경우를 별도의 adapter로 관리하는 것이 명확하다.

일반적으로 레포지토리에서는 엔티티를 다루고 서비스와 컨트롤러에서는 DTO를 사용한다. 그러나 외부 영역의 데이터는 거친입자로 타 서비스의 영향을 받을 수 있는 형태여도 되지만 내부여역은 비즈니스 로직을 명확히 다룰 수 있는 고운 입자인 도메인 모델을 사용하는 것이 바람직하다.

따라서 서비스에서도 엔티티를 반환하게하고 컨트롤러에서는 엔티티를 DTO로 변환해서 사용한다.

외부 영역

대출 서비스에서 도서 대출과 반납을 하고 결과에 따라 도서 서비스에서는 해당 책의 상태를 대출 가능, 불가능으로 변경해야한다.

일반적인 구조라면 그냥 하나의 애플리케이션안에서 상태를 변경하는 객체간의 메시지를 보내서 처리하면 되지만 서비스가 분리되어있기 때문에 이러한 이벤트를 비동기로 전송하여 비즈니스 일관성을 맞출 수 있다.

이렇게 서비스가 독립성을 유지하면서 다른 서비스의 이벤트 변화를 기반으로 각 서비스를 연계해서 구성하는 것을 EDA라고 한다.

대출이 발생하면 대출 서비스의 구현체인 RentalServiceImpl에서 아웃바운드 어댑터인터페이스인 RentalProducer를 호출하고 그 구현체인 RentalKafkaProducerImpl가 StockChanged 이벤트를 발행한다.

그러면 도서 서비스에서는 구독하고 있던 BookKafkaConsumer에서 해당 이벤트를 받아 내부의 BookService를 호출해서 도서의 대출상태를 변경해 비즈니스 일관성을 맞춘다.