티스토리 뷰

SW 개발/Java

JHipster, 개념 및 사용법 정리

지단로보트 2022. 7. 4. 11:38

개요

  • JHipsterJava 진영에서 모놀리스 또는 마이크로서비스를 제작하기 위한 만물상자라고 할 수 있다. JHipster의 커맨드 도구를 이용하여 프로젝트를 생성하면 순식간에 최신 모던 기술 스택 기반의 모든 기술을 총망라한 프로젝트를 제작할 수 있다. 개발자는 순전히 선택한 모던 기술 스택을 숙지하기만 하면, 곧바로 비지니스 로직 구현에 집중할 수 있는 장점이 있다. 이번 글에서는 JHipster를 구성하는 기본 철학 및 개념에 대해서 설명하고자 한다.

JHipster Registry

  • Netflix Eureka 기반의 서비스 디스커버리 애플리케이션이면서 Spring Cloud Config 서버 애플리케이션이다. 기본값으로 HTTP 8761 포트를 사용한다. [관련 링크]
  • JHipster 생태계에서 가장 최우선 순위로 기동되어야할 핵심 애플리케이션이라고 할 수 있다. JHipster API Gateway, JHipster Microservice 기반으로 생성된 모든 애플리케이션이 JHipster Registry을 기반으로 작동한다.
  • 모놀리스를 포함한 모든 애플리케이션에서 분산 캐시를 이용한 스케일링을 가능하게 해준다.
  • Spring Cloud Config 서버 기능을 통해 모든 애플리케이션의 환경 변수를 중앙의 JHipster Registry가 통제할 수 있다. 대표적으로 JWT 토큰 생성 및 유효성 검사에 사용되는 Secret Key를 저장할 수 있다.
  • 클라이언트가 되는 애플리케이션에 요구되는 환경 변수는 아래와 같다.
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster-registry-password}@{jhipster-registry-host}:8761/eureka/
SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster-registry-password}@{jhipster-registry-host}:8761/config/

JHipster Registry: 운영체제 환경 변수 설정

  • JHipster Registry 기동시 적용 가능한 운영체제 환경 변수 설정의 예는 아래와 같다.
# JVM 옵션
_JAVA_OPTIONS=-Xms512m -Xmx512m

# 프로파일
SPRING_PROFILES_ACTIVE=prod

# admin 계정 비밀번호 (생략시 admin으로 기본값 설정)
SPRING_SECURITY_USER_PASSWORD=admin

# 생성될 JWT 토큰의 Secret Key
# 중복 관리를 지양하기 위해 일반적으로 아래 Spring Cloud Config 환경 변수로 별도 분리
JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET={secret}

# 애플리케이션 기동시 적용할 중앙 환경 변수 위치
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI=https://{git-repository-uri}
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_PATHS=central-config

JHipster Registry: Eureka Server 이중화 설정

  • JHipster Registry을 이중화하는데 있어 고려할 문제는 Eureka Server의 특성으로 인해 로드 밸런서 뒤의 n개 노드 구성이 불가능하다는 것이다. 따라서 도메인부터 다른 독립적인 노드 2개를 개별로 구성하고 서로가 Eureka Server이면서 다른 노드의 Eureka Client가 되어야 한다. (이를 Peer Aware Mode라고 부른다) JHipster Registry에서는 아래와 같이 환경 변수를 설정한다. [관련 링크]
# 1번 노드에서는 2번 노드의 URL을 지정
SPRING_PROFILES_ACTIVE=prod
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster-registry-password}@{jhipster-registry-host-2}:8761/eureka/

# 2번 노드에서는 1번 노드의 URL을 지정
SPRING_PROFILES_ACTIVE=prod
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster-registry-password}@{jhipster-registry-host-1}:8761/eureka/
  • Eureka Client 애플리케이션에서는 아래와 같이 환경 변수를 설정한다. [관련 링크]
# 클라이언트에서는 2개의 JHipser Registry 인스턴스를 콤마(,)로 구분하여 등록
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster-registry-password}@{jhipster-registry-host-1}:8761/eureka/, http://admin:$${jhipster-registry-password}@{jhipster-registry-host-2}:8761/eureka/

JHipster Registry: ECS Fargate 트러블슈팅

  • ECS Fargate 환경에서는 Eureka Server의 이용이 까다로운 편이다. Eureka Client는 내부적으로 자신의 호스트명으로부터 프라이빗 IP 주소를 획득하고 이것을 Eureka Server에게 알리는 원리인데, ECS Fargate 환경에서는 내부 IP 주소 획득이 정상적으로 동작하지 않아 우회 방법이 필요하다.
  • 아래와 같이 entrypoint.sh 파일을 작성한다. ECS Fargate가 제공하는 ECS_CONTAINER_METADATA_URI 환경 변수를 이용하여 프라이빗 IP 주소를 획득하고, hostname 명령으로 호스트명을 획득하여 둘을 맵핑한 정보를 /etc/hosts 파일에 갱신하는 역할을 수행한다.
#!/bin/sh
export ECS_INSTANCE_IP_TASK=$(curl --retry 5 -connect-timeout 3 -s ${ECS_CONTAINER_METADATA_URI})
export ECS_INSTANCE_IP_ADDRESS=$(echo ${ECS_INSTANCE_IP_TASK} | python3.9 -c "import sys, json; print(json.load(sys.stdin)['Networks'][0]['IPv4Addresses'][0])")
echo "${ECS_INSTANCE_IP_ADDRESS} $(hostname)" | sudo tee -a /etc/hosts
echo "ECS Instance IP Response: " ${ECS_INSTANCE_IP_ADDRESS}
  • 다음은 Dockerfile 파일을 작성하는 예이다. JHipster Registy 서버에서 앞서 작성한 entrypoint.sh을 최종적으로 실행시키는 역할을 수행한다.
FROM docker.io/jhipster/jhipster-registry:v7.1.0
EXPOSE 8761
USER root
RUN apt update -y
RUN apt install sudo -y
RUN apt install software-properties-common -y
RUN add-apt-repository ppa:deadsnakes/ppa -y
RUN apt install python3.9 -y
COPY entrypoint.sh /
USER 0
ENTRYPOINT ["sh", "/entrypoint.sh"]

JHipster Registry: Spring Cloud Config 환경 변수 파일 위치 지정

  • JHipster Registry를 기동할 때 아래와 같이 운영체제 환경 변수로 Spring Cloud Config 환경 변수 파일의 위치를 지정할 수 있다.
# native 방식, 로컬 파일시스템에서 환경 변수를 로드
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=native
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS=file:./central-config

# git 방식, Git 저장소에 저장된 파일에서 환경 변수를 로드
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI=https://{git-uri}
SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_PATHS={file-name}
SPRING_CLOUD_CONFIG_SERVER_GIT_USERNAME={git-username}
SPRING_CLOUD_CONFIG_SERVER_GIT_PASSWORD={git-password}
SPRING_CLOUD_CONFIG_SERVER_GIT_DEFAULT-LABEL={git-branch}
  • JHipster Registry의 등록된 모든 애플리케이션이 기동될 때 적용되는 환경 변수의 우선순위는 아래와 같다.
# 1순위, 특정 appname의 특정 profile에만 적용
{appname}-{profile}.yml

# 2순위, 특정 appname의 모든 profile에 적용
{appname}.yml

# 3순위, 모든 애플리케이션의 특정 profile에만 적용
application-{profile}.yml

# 4순위, 모든 애플리케이션의 모든 profile에 적용
application.yml

JHipster API Gateway

  • JHipster API GatewaySpring Cloud Load Balancer 기반의 API Gateyway 애플리케이션이다. Netflx Hystrix 기반의 서킷 브레이커 기능을 제공한다. 기본값으로 HTTP 8080 포트를 사용한다. [관련 링크]
  • 앞서 언급한 EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE 환경 변수를 설정하면 원격지의 JHipster Registry 서버에 클라이언트 인스턴스로 등록된다.
  • JHipster Registry에 등록된 인스턴스 정보를 기반으로 클라이언트 사이드 로드 밸런서 기능을 수행한다.
  • JHipster API Gateway를 경유하는 마이크로서비스의 생명 주기는 아래와 같다.
1. JHipster Microservice가 새로 실행된다.
2. JHipster Registery에 새로 실행된 마이크로서비스가 등록된다.
3. JHipster API Gateway는 외부 API 요청에 대해 아래 주소 패턴에 따라 적합한 마이크로서비스에 요청을 라우팅한다.
https://{api-gateway-uri}/services/{app-name}/api/{app-specfic-request-path}

4. JHipster Microservice가 종료된다.
5. JHipster Registery에서 종료된 마이크로서비스를 제거한다.

JHipster API Gateway: 프로젝트 생성

  • JHipster API Gateway 기반 프로젝트를 생성하는 방법은 간단하다. jhipster(또는 khipster) 명령 실행시 Gateway application 옵션을 고르면 된다. [관련 링크]

JHipster API Gateway: 운영체제 환경 변수 설정

  • JHipster Gateway 기동시 적용 가능한 운영체제 환경 변수 설정의 예는 아래와 같다. (Amazon ECS on AWS Fargate 배포 환경을 가정했는데, 아래 환경 변수에 대응되는 도커 이미지 빌드 방법은 본 블로그의 이 글을 참고한다.)
SPRING_PROFILES_ACTIVE=prod,cognito,api-docs
MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true
SPRING_SECURITY_USER_PASSWORD=admin
JHIPSTER_REGISTRY_PASSWORD=admin
JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET={secret}
JHIPSTER_REGISTRY_HOST_1={register-1-uri}
JHIPSTER_REGISTRY_HOST_2={register-2-uri}
EUREKA_INSTANCE_PREFER_IP_ADDRESS=true
EUREKA_INSTANCE_HOSTNAME=${ecs-instance-hostname:localhost}
EUREKA_INSTANCE_PREFER_IP_ADDRESS=true
EUREKA_INSTANCE_IP_ADDRESS=${ecs-instance-ip-address:127.0.0.1}
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:${jhipster-registry-password}@${jhipster-registry-host-1}:8761/eureka/, http://admin:${jhipster-registry-password}@${jhipster-registry-host-2}:8761/eureka/
SPRING_CLOUD_CONFIG_URI=http://admin:${jhipster-registry-password}@${jhipster-registry-host-1}:8761/config
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI={amazon-cognito-user-pool-url}
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID={amazon-cognito-user-pool-client-id}

JHipster Microservice

  • JHipster Microservice는 실제 핵심이 되는 비지니스 로직을 작성하는 애플리케이션이다. 기본값으로 HTTP 8081 포트를 사용한다. [관련 링크]

KHipster

  • KHipsterKotlin 언어로 JHipster 프로젝트 생성을 가능하게 해주는 공식 블루프린트이다. 기본 개념 및 자세한 사용법은 본 블로그의 이 글을 참고한다.

인증, 인가 연동

  • JHipsterSpring Security를 기반으로 총 3가지 선택지의 인증, 인가 기능을 제공한다. 첫번째, JWT, 두번째, Session, 마지막으로 세번째가 외부 idP 연동이다.
  • 요즘 프로덕션 레벨의 개발 추세는 인증, 인가를 직접 구현하지 않고, idP(Identity Provider)를 연동하여 개발하여 생산성을 높이는 것이다. 대표적으로 Amazon Cognito, Okta, KeyColak이 존재한다.
  • Registry, Gateway, Microservice 모두 idP를 연동할 수 있다. 아래와 같이 환경 변수를 설정하면 된다. (참고로 Registry에서 idP연동을 원하지 않을 경우 위 환경 변수와 oauth2 프로파일을 제거하면 기본 설정된 admin 계정으로 로그인이 가능하다.)
# OpenID Connect Provider의 Issuer URI를 입력 (ex: Amazon Cognito, Okta, KeyCloak)
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=https://cognito-idp.{region}.amazonaws.com/{user-pool-id}

# OpenID Connect Provider에 등록된 Client ID를 입력
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID={app-client-id}

# OpenID Connect Provider에 등록된 Client Secret을 입력 (Client Secret이 없을 경우 생략)
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET={app-client-secret}
  • 위 설정을 적용하는 것 만으로 Gateway로 유입되는 모든 요청에 대해 기본적인 인증, 인가가 작동한다. 규칙에 대한 커스터마이징을 원하면 SecurityConfiguration 클래스를 수정하면 된다. 인가 규칙에 대한 수정은 아래와 같이 Spring Security의 문법을 따른다. [관련 링크]
1. SecurityConfiguration 클래스에 작성
    .antMatchers("/api/foobars").permitAll()
    .antMatchers("/api/**").authenticated()
    .antMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN)

2. 각 컨트롤러의 public 메써드 레벨에 작성
    @PreAuthorize("hasRole('" + AuthoritiesConstants.ADMIN + "')")
    @PreAuthorize("hasAnyRole('" + AuthoritiesConstants.ADMIN + ','" +  AuthoritiesConstants.USER + "')")
1. Amazon Cognito에서 User Pool을 생성한다.
2. Amazon Cognito에서 App Client를 생성한다.
3. Amazon Cognito의 App Client에 [Domain], 로그인 결과를 수신할 [Callback URLs], 로그아웃 후 리다이렉트할 [Sign out URL]을 입력한다. (클라이언트가 되는 프론트엔드의 주소를 입력)
4. 클라이언트가 되는 프론트엔드에게 [Region], [User Pool ID], [App Client]의 [Client ID](존재할 경우 [Client Secret]까지)를 전달한다.
5. 클라이언트에서는 Amplify 라이브러리를 이용하여 회원 가입, 로그인, 로그아웃을 구현한다.
6. 클라이언트는 Amazno Cognito에 로그인 완료 후 획득한 [Access Token]을 Authorization: Bearer 헤더에 첨부하여 Gateway에 REST API를 요청한다.

참고 글

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함