AWS 아키텍트

https://aws.amazon.com/ko/certification/certification-prep/

 

AWS Certification 시험 준비

AWS 공인 고급 네트워킹 – 전문 분야 공식 시험 학습 안내서는 AWS 전문가가 작성합니다. 이 안내서는 시험 목표를 다루며, AWS 공인 고급 네트워킹 전문가로서 겪을 수 있는 상황을 토대로 실습할 수 있도록 하여 시험 환경에서 네트워킹 기술을 입증하도록 준비하는 데 도움이 됩니다. AWS를 사용한 클라우드 기반 솔루션 설계, 개발 및 배포에서 도구를 활용한 AWS 네트워킹 작업 자동화까지, 이 안내서는 AWS와 관련된 프로세스와 모범 사례를 습득하는

aws.amazon.com

특히 무료 온라인 강의가 있다. Exam Readiness항목에 무료 디지털 교육을 클릭하면 들을 수 있다.

 

https://aws.amazon.com/ko/training/path-architecting/

 

학습 경로 - 아키텍트

다운로드가 가능한 램프업 안내서는 아키텍트 역할을 수행하는 개인이 무료 디지털 교육, 강의실 과정, 비디오, 백서, 자격증 및 AWS Cloud 지식 구축에 도움이 되는 기타 정보를 모두 볼 수 있도록 합니다. 안내서 다운로드 »

aws.amazon.com

Architect과정의 자격증은 associate, professional 두 가지가 있다. 아래 후기에서 처럼 asso는 150달러 pro는 300달러의 응시료가 있다. 그리고 합격 한다면 50% 할인 바우처 1개를 주는데 asso + pro = $150 + ( $300 / 2 ) = $300이고, pro = $300이다. 결과적으로 associate를 따고 pro를 따는 것과 바로 pro를 따는 것은 같은 가격이다. 어떤 path를 선택하던지 개인이 선택하면 될 것 같은데... 나는 시험공부는 길어지는 걸 싫어해서 단기간 집중하려고 한다. 지금까지 프로젝트를 하면서 사용했던 것들도 시험내용과 비교해보고 앞으로 진행할 프로젝트에도 도움이 될 것이라 기대된다.

후기 블로그 : https://goddaehee.tistory.com/194

 

[AWS] 자격증 준비 및 접수방법, 합격 후기 (자료 첨부)

[AWS] AWS 자격증 준비 및 접수방법, 합격 후기 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ AWS Certification 준비 및 합격후기 ] 입니다. : ) SQLD에 이어 약 2년만에 준비 하게된 시험이네요. 많은 분들이 AW..

goddaehee.tistory.com

 

앞서 인증서를 만들다 보니 CA인증서 밑에 중간 인증서, 중간 인증서 밑에 서버 인증서... 이렇게 상위 하위 인증서 개념이 있었다. 결론부터 보자면

이렇게 server 밑에 sub.server인증서가 있는 것이다. 어떻게 하위 인증서를 만들 수 있을까?

내가 알고 있는 방법은 2가지 방법이다.

  1. 상위 key pair (여기선 server를 지칭함.) 하위 key pair(sub.server)를 생성하는 방법
  2. 하위 key pair에서 CSR을 발행해서 상위 key pair가 CSR에 sign하는 방법

먼저 상위 key pair(server)에서 바로 key pair를 생성하는 것이다. 

 

아주 간단하게 필요한 정보만 넣으면 만들어 진다. sub.server키를 export하면 .p12 형식으로 만들어서 전달 가능하다.

 

다음 방법은 CSR을 사용하여 sub.server키를 만들어 보자. 이 방법은 전 방법과는 다르게 키를 만드는 주체가 서로 다르다는 것이다. 서로 다른 서버에서 각각 키를 만들었는데 어떻게 sign을 하는지가 포인트다.

서로 다른 JKS 키가 있다. 각각 key pair를 살펴보면 아래와 같이 전혀 상관없는 key pair이다.

이 상태에서 sub.server키에서 CSR을 생성한다. p10형식의 .csr파일이 생성된다.

이제 만들어진 CSR 파일로 server키에서 sign을 하면 된다.

CSR에 대한 응답으로 .p7r파일이 생성된다.

sub.server키에서 import CA reply를 한 결과 위와 같이 설정된다.

캡처가 장황하게 들어가서 길어졌지만 CSR을 간단하게 설명하면 sub키에서 CSR을 생성하면 server키에서 .p7r로 sign을 하고 다시 sub키에서 .p7r를 import 하면 된다.

openssl script로 뭐가 뭐지? 하며 정리가 안되던 차에 keystore explorer로 눈에 보이게 캡처를 하나하나 떠서 만들어 정리해봤다. 혹시라도 헷갈리시는 분은 이렇게 정리하고 script를 다시 보면 이해가 가지 않을까 싶다.

지금 운영되는 서버에 인증서가 만료가 되었다. 서로 다른 도메인의 서버끼리 2way 인증을 하는데는 서로의 certification을 가지고 있어야 한다. Java Key Store는 key pair와 trusted certificate를 담을 수 있는 그릇이다.

먼저 갱신한 인증서를 확인해보자. 

server측 인증서
Client측 인증서

서버쪽은 CA인증서로부터 발급받은 key pair이고, 클라이언트는 self-signed key pair이다. 이 상태로는 2way ssl 인증이 안된다. 서로의 certification을 교환해서 가지고 있어야 된다.

 

x.509형식의 .cer파일을 클라이언트 JKS에 import하자.

클라이언트 JKS 최종 결과

마지막 그림처럼 Trusted Certificate로 server의 certificate가 추가 되었다.

그럼 반대로 클라이언트 인증서를 서버 JKS에 추가하는 것도 같은 방식으로 진행하면 된다.

.cer 파일로 추출된 클라이언트 인증서를 서버JKS에 trusted 인증서로 등록하자.

서버 JKS 최종 결과

이제 서버에서 JKS 관련 설정을 해보자. application.yml에서 아래와 같이 설정한다.

application.yml

설정 키 값만 보더라도 뭘 의미 하는지는 알 수 있을거라 믿는다. 이 설정 중에 주목해야 할 것이 하나 있는데 바로 server.ssl.client-auth : need 항목이다. 이 설정이 있어야 약속된 인증서(Trusted Certificate)를 들고 접속하는 클라이언트인지 아닌지 체크를 하게 되어있다. 역시 spring boot... 설정만으로 mutual ssl 인증이 동작하는 구나...

참고 사이트 : https://www.naschenweng.info/2018/02/01/java-mutual-ssl-authentication-2-way-ssl-authentication/

 

Java mutual SSL authentication / 2-way SSL authentication

Despite SSL being widely used, Java mutual SSL authentication (also referred to as 2-way SSL authentication or certificate based authentication) is a fairly simple implementation when understanding the key concepts of how mutual SSL authentication works. T

www.naschenweng.info

openssl을 이용한 script에 대한 내용은 많아서 Keystore Explorer를 이용하여 캡쳐를 만들어 보았다. 하지만 개인적으로는 (git 사용도 마찬가지로) GUI보다 CLI가 더 안정적이고 풍부한 기능을 제공하니 되도록 CLI를 사용하는 버릇을 들이는게 좋을 듯 하다.

'Programming > Spring Framework' 카테고리의 다른 글

Spring Boot Auto Configuration 예제  (0) 2020.04.21
코딩 시험과 TDD  (0) 2019.02.14
Annotation-based Controller  (0) 2017.02.01

회사에서 윈도우 노트북을 사용한다. 그래서 우분투 특히 bash를 사용하려면 아래와 같은 방법을 사용했다.

  1. vmware, virtual box에 우분투를 설치해서 사용
    • guest os를 깔아서 사용하기 때문에 무겁게 느껴진다...
    • 사실 desktop보다는(bash가 아닌 GUI가 들어있으니 느려도 그려러니...) server를 사용하기 때문에 가볍게 구동되길 바라서인지 더 무겁게 느껴지는 것 같다.
  2. build server에 ssh, smb를 붙여서 사용
    • ssh, smb 콤비는 내가 정말 사랑하는 조합
    • 빌드 서버는 보통 랩탑보다 사양이 좋다. 그래서 dev 서버나 도커 빌드 및 실행 서버로 사용한다.
    • 다만 사내망은 분리되어 있어서 외부 접속이 어렵다.
      • 자택근무를 하다보니 외부접속이 안되는 것이 더 아쉽다.
  3. Linux Containers on windows 10
    • Hyper-V에서 실행되는 LinuxKit 기반 가상 컴퓨터를 사용하여 실행
      • docker 클라이언트는 window 데스크탑에서 실행되지만 linux VM상의 Docker daemon을 호출합니다.Moby VM의 linux 컨테이너
    • Linux Containers with Hyper-V isolation
      • Hyper-V isolation 기능이 있는 linux컨테이너는 컨테이너를 실행하기에 충분한 OS만으로 최적화된 linux VM에서 각 linux 컨테이너를 실행
        • microsoft/nanoserver, openjdk:8-jdk-alpine같은 tiny build를 말하는 것 같다.
    • 참조 : https://docs.microsoft.com/ko-kr/virtualization/windowscontainers/deploy-containers/linux-containers
  4. WSL (Windows Subsystem For Linux)
    • 진짜 Ubuntu가 설치되니까 실제 개발 환경과 유사하다.
      • 1주 정도 써보니 좀 불편하네요. 정신 건강을 위해서는 그냥 vm환경으로 하라는 조언도 있습니다. ^^;
    • vmware나 virtual box와는 다르게 부팅 없이 바로 실행된다.
    • 느린 IO 속도, 커널이 다 호환되지 않아서 일부 app(nmap등)이 실행되지 않는다.
    • 하지만 단점들이 개선된 WSL2가 windows 참가자 프로그램에 리뷰중이니 정식 배포가 조만간 될 수 있을 것 같다. 그전에 먼저 사용하고 싶으신 분은 아래 링크에서 설치 해보시길....

어제는 WSL로 docker, ssh를 사용해서 내 개인 서버의 let's encrypt 인증서를 와일드 카드로 갱신했다.

docker도 잘 돌고 (https://medium.com/rkttu/wsl%EC%97%90%EC%84%9C-native-docker-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0-ff75b1627a87) ssh를 통해 remote 환경에서도 작업하는데 문제가

없었다. => 이렇게는 현재 wsl에서는 동작하지 않는다.

https://blog.jayway.com/2017/04/19/running-docker-on-bash-on-windows/에 나와 있는데로 docker 데몬은 window것을 쓰고 wsl이 docker client가 되는 방법이다.

win10 1607버전 이상이라고 하니 3~4년 전에 있었는데 이제야 알았다. 되니까... 이러고 다른 방법은 고민을 안했던거 같다. wsl뿐만 아니라 다른 것들도 현실에 안주하지 말고 더 좋은 방법은 없는지 고민해 보는게 필요할 듯 하다.

p.s. 여담인데 wsl2는 아직 preview 버전에서 동작하는데 불안정 버전이라 그런지 이런 상황도 있었다. 쫄지 말자. ㅎ

WSL2는 가상 네트워크 스위치 설정이 잘 안되어서 결국 못쓰게 되었다.

참고글 https://m.blog.naver.com/seongjin0526/221778212779

Docker에서도 settings에서 WSL2를 반영 할 수 있도록 지원하고 있다. 다만 아직 preview이다. (2020.05.15)

현재 WSL1으로 사용하는 용도

'Operating System' 카테고리의 다른 글

Ubuntu 16.04 server ‘lvmetad is not active yet' solution  (0) 2017.04.21
Ubuntu 원격접속 (xrdp, vnc)  (0) 2016.06.09
Ubuntu 14.04 LTS 설치  (0) 2014.05.30

1. 아키텍트 역할 정의

이러한 조직이 MSA로 가기 위하여 가장 고민해야 할 부분은 아키텍트들이다. 개발자들은 대부분 개발을 하고 공통 프레임 워크를 사용하고, 강력한 표준 준수를 규정으로 정의하여 자율성을 최대한 적게 가져갔던 Monolithic 아키텍처 대비 개발자가 거의 모든 일을 알아서 해야 하는 그리고 자 율도가 많은 MSA 특성에 맞는 시스템을 구현하기 위하여 아키텍트의 역할은 많은 변화가 필요하다.
"Building microservices Sam Newman" 책에서는 아키텍트의 역할을 "진화적 아키텍트"라는 용어를 사용하여 정의하였다.
진화적인 아키텍트는 [원칙과 실천]으로 분리하여 아키텍트 역할을 정의해야 한다고 했다.

  • 원칙 : 변하지 않는 것으로 한 프로젝트 혹은 한 회사의 아키텍처 원칙을 정의한다. 예를 들면 우리의 아키텍처는 "외부 의존성은 낮추고, 복잡성은 제거 해야 하며, 인터페이스와 데이터의 흐름은 일관성을 유지한다" 라는 원칙을 세웠다고 하 자.
  • 실천 : 위의 원칙을 지키고자 하는 여러 아키텍처를 활용하는 것이다. 예를 들면 "표준 인터페이스는 Rest API, 통합 DB 는 사용하지 않으나 업무 종류별 회사 표준의 DB를 활용 한다" 등등. 각 시스템에 맞는 그러나 표준은 준수하는 실천을 해 야 하는 것이다.

아키텍트는 이렇게 원칙을 정의하고 실천은 알아서 하되, 이를 원칙에 맞게 조율하고 준수하게 하는 통합 아키텍트와 각 서비스에서 아키텍처를 구현하는 서비스 영역내 아키텍트로 구분하여 역할을 만들어야 한다.
[출처]MSA 어플리케이션 구축 시 조직 구성은 어떻게 해야 할까요?|작성자리니

원칙을 정하고 원칙에 의해 아키텍처를 적용하면 진행방향이 흔들리지 않을 것입니다.

2. TDD

  1. 프로그램을 개발하기 전에 먼저 테스트 코드를 작성하는 것...이라고 하지만 개인적인 생각은 꼭 "먼저"일 필요는 없다이다. 이것이 test code가 필요없다는 것이 아니라 프로그래밍 시작이건 중간이건 끝나고던 배포전에만 testcode가 작성되어 있다면 된다는 것이다. 그러 나 먼저 하는 것을 추천한다. 항상 할 일을 미루면 프로그램 중에도 찝찝하고 한 번 미룬건 계속 미루기 쉽상이기 때문이다.
  2. TDD를 통해서 얻고자 하는 최종 목적은 잘 동작하는 깔끔한 코드(clean code)이다. 이는 유지보수의 편의성, 가독성, 안정성등의 여러가 지 의미를 내포한다.
  3. TDD 진행
    1. 과정 질문(Ask) : 테스트 작성을 통해 시스템에 질문한다.(테스트에 실패할만한 질문을 해야한다.)
    2. 응답(Response) : 테스트를 통과하는 코드를 작성해서 질문에 대답한다. (테스트에 통과할 최소의 코드만을 작성해야 한다.)
    3. 정제(Refine) : 아이디어를 통합하고, 불필요한 코드를 제거하고 모호한것을 명확히한다.
    4. 반복(Repeat) : 다음 질문을 통해 계속해서 진행해 나간다.

3. CI/CD Automation

  1. CI (Continuous Integration)
    1. 개발자가 자신의 코드를 중앙 코드와 통합하면 자동으로 빌드되고 report가 생성된다.
    2. 이때 위의 TDD에서 작성한 test code의 coverage에 따라 영향을 받는다.
  2. CD (Continuous Delivery)
    1. 사실상 CI의 연장선, CD를 하려면 CI가 선행되어야 한다고 봐도 무방하다.
    2. 현재 우리 회사는 Jenkins가 사용되고 있으므로 검토대상 1호이다.

4. Container Management

  1. Kubernetes같은 orchestration tool이 하는 일이다.
  2. 하지만 현실적으로 cloud의 기능을 사용해야 할 듯 하다.
  3. resiliency 복원력 정도로 해석할 수 있다. 다음 이야기 하는 service mesh와 함께 문제가 되는 container를 다시 동작하도록 하는 기능이 있다.

5. Service Mesh

  1. 밑에 그림 처럼 outer gateway가 아닌 inner gateway에서 적용되어야 할 부분이다.
  2. AWS App Mesh
    1. https://docs.aws.amazon.com/ko_kr/app-mesh/latest/userguide/what-is-app-mesh.html
  3. Azure service Fabric Mesh
    1. https://docs.microsoft.com/ko-kr/azure/service-fabric-mesh/service-fabric-mesh-overview
  4. Spring Cloud Consul
    1. Consul은 full featured control plane을 제공하는 service mesh solution.
    2. service discovery, health checking, key-value store, secure service communication, multi datacenter
    3. https://www.consul.io/intro/index.html
      1. Consul vs. Other Software
        1. 다른 소프트웨어와의 비교를 consul입장에서 정리한 것 같다.
  5. Istio
    1. load balancing, service registry, API management등의 기능이 있는 차세대 service mesh
    2. 그럼 흔히 알고 있는 netflix OSS의 service mesh와는 뭐가 다르길래 차세대라고 하는건지?
      아래 그림을 참고하자.
      1. 비즈니스 로직에 service mesh 관련 코드가 있는지 없는지가 큰 차이다.
      2. 항상 코드에 섞여 있는 것은 (특히 인프라 부분) 좋은 상황은 아니다.
      3. 그래서 properties나 yaml이라도 사용하여 소스 코드와 분리하려고 하지 않는가.

6. Distributed Tracing

  1. AWS App Mesh
    1. https://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-guide.pdf
  2. Azure Application Insight
    1. https://docs.microsoft.com/ko-kr/azure/azure-monitor/app/app-insights-overview
  3. Spring Sleuth - zipkin을 이용
    1. trace, span ID를 이용하여 remote logging을 하고 micro service간의 log들을 추적할 수 있다.
    2. 위의 두 서비스에 비해 분명 개발 및 운영도 해야 되어서 손이 많이 가지만 가격적인 측면도 무시 못한다.
    3. 이러한 tracing서버는 한 번 구축 하면 다른 서비스도 적용 가능해서 어느 정도 서비스가 많아 지면 손익 변곡점을 넘기게 마련이다. 중장기적인 플랜을 기준으로 판단이 필요하다.

그냥 간단하게 생각 나는 것들을 정리해 보았습니다. log에 대한 visualize라든가 ELK를 이용하는 것들이 더 생각 하는데 조금씩 살을 덧붙여 처음 시작할 때는 고려사항 목록으로, 진행하면서는 check list로 활용하도록 해볼 생각 입니다.

 포스팅에서는 MSA에 접근하는 Client, 운영자, 개발자측면의 흐름도를 살펴보도록 하겠습니다.


Microservice는 SOA(Service Oriented Architecture)의 경량화 버전으로 (Service: 특정 기능의 집합, service의 범위 정의가 중요) 모놀리틱 아키텍처(monolithic architecture)를 쪼개서 독립적으로 구분합니다.

Microservice는 독립적으로 디플로이/확장될 수 있는 서비스들을 조합하여 large 어플리케이션을 구성하는 아키텍처 패턴입니다.

일반적으로 Service Discovery, API Gateway, Orchestration, Choreography, Context Boundary등의 서비스들의 조합으로 이루어져있습니다.

Netflix, Twitter, Amazon, Nike 등의 회사에서 채택한 아키텍처로 소개되면서 '14년 초반부터 현재까지 주목 받고 있습니다. 아키텍처 관련 기술 표준은 없으며 SW벤더들이 기존 제품군(SOA, PaaS, ...)을 Microservices를 지원하기 위한 플랫폼으로서 rebrand하는 추세로 전환되고 있습니다.

 

MSA 상세 아키텍처 패턴 분석 (gartner 자료)

[그림 1] Microservices Architecture Components @ 2018 Gartner, Inc.

 

본 이미지는 Gartner에 게시된 Microservice Architecture Components입니다.

앞서 살펴보았단 표준 아키텍처(개인적인 생각)를 보다 도식화하여 상세히 풀어 놓았습니다.

 

 관리 컨테이너 : 개별 서비스 인스턴스에는 작동 할 컨텍스트가 필요합니다. 가상 컴퓨터, Docker 컨테이너 또는 조정 된 프로세스로 구현 된 관리 컨테이너는 이러한 기능을 제공합니다. 이 구성 요소는 인스턴스 관리 및 조정을 제공하고 필요에 따라 새 인스턴스를 회전하며 개별 인스턴스의 수명주기를 관리합니다.

 외부 게이트웨이 : MSA 구현은 비즈니스 응용 프로그램 및 응용 프로그램에서 사용할 수있는 API 형태로 기능을 노출 할 수 있습니다. 서비스 외부 게이트웨이는 이러한 서비스에 대한 액세스를 관리하고 트래픽 관리 및 보안 정책을 적용하여 마이크로 서비스 환경을 보호합니다. 외부 게이트웨이 기능은 종종 API 관리 제품을 사용하여 구현됩니다.

 서비스 메쉬 기능 : 서비스 메쉬는 서비스 간의 통신을 느슨하게 결합, 신뢰성 및 유연성을 유지하는 데 도움이되는 기능으로 구성됩니다. 이러한 기능을 통해 서비스 분리, 버전 관리 전략 지원 및 부하시 탄성 확장 성 관리가 가능합니다.

서비스 라우팅 : 클라이언트 응용 프로그램에서 또는 마이크로 서비스 사이의 요청은 구성 및 정책에 따라 올바른 마이크로 서비스로 라우팅해야합니다.

로드 밸런싱 : 각 마이크로 서비스의 인스턴스는 확장 성을 지원하기 위해로드 밸런싱이 필요하며로드 밸런싱의 세밀성 및 구성은 각 서비스를 관리하는 팀에 의해 제어되어야합니다.

서비스 발견 : 서비스는 느슨하게 결합 된 방식으로 검색 가능해야합니다. 서비스 검색은 일반적으로 서비스 레지스트리를 사용하여 구현되며,이 서비스 레지스트리에서 마이크로 서비스 소유자는 런타임에 다른 서비스가 필요로하는 정보를 등록 및 구성하여 찾아서 호출 할 수 있습니다. 이것을 네트워크 수준의 DNS와 유사하게 생각할 수 있습니다. 서비스 발견은 또한 마이크로 서비스간에 존재할 종속성을 관리하는 데 도움을 주며 환경 변화를 관리 할 때 중요합니다.

구성 저장소 : 서비스 인스턴스는 마이크로 서비스와 전체 환경과 관련된 구성을 공유해야합니다. 예를 들어 환경에 배포 된 마이크로 서비스에는 서비스 검색 레지스트리의 위치와 로그 이벤트를 내보내는 위치를 파악하는 방법이 필요합니다. 마이크로 서비스 환경의 분산 특성으로 인해 분산 키 - 값 저장소를 사용하여 구현되는 경우가 많습니다.

ID 공급자 : 서비스 인스턴스는 신뢰할 수있는 ID를 사용하여 통신해야합니다 ( "ID를 Microservices로 작성"참조 ). 서비스 메시는 이러한 ID를 제공하고 유효성을 검사합니다. 여기에는 외부 ID 공급자 또는 디렉터리와의 통합이 포함될 수 있습니다.

 서비스 이미지 레지스트리 : 사용자 환경의 어딘가에는 빌드되고 테스트 된 서비스의 불변 이미지를 저장하는 레지스트리가 있습니다. 이 저장소에 사용되는 기술은 사용하는 배포 단위에 따라 다릅니다. 코드 저장소 (동적으로 생성 된 서비스의 경우), Docker 이미지 레지스트리, 이진 아티팩트 저장소 또는 VM 이미지의 BLOB (Binary Large Object) 기반 저장소 일 수 있습니다.

 메시지 지향 미들웨어 : 가장 간단한 MSA 구현은 HTTP와 같은 동기식 프로토콜 또는 gRPC 또는 Thrift와 같은보다 효율적인 프로토콜을 사용하여 지속 가능할 수 있습니다. 그러나 대부분의 MSA는 게시 / 가입 및 화재 및 잊어 버리기와 같은 이벤트 및 메시지 중심 패턴을 지원하기 위해 비동기 메시징 채널이 필요합니다.

 빌드 및 테스트 자동화 : MSA의 개발 민첩성 이점은 개발 출력 품질을 극대화하고 전달을 간소화하기 위해 개발주기에서 높은 수준의 빌드 및 테스트 자동화가 필요합니다.

 배포 자동화 : 개발 민첩성 이점을 완전히 실현하려면 배포를 자동화해야합니다. 새롭거나 향상된 마이크로 서비스의 배포를 자동화 할뿐만 아니라 외부 아키텍처 자체의 변경 자동화 (예 : 배포의 일부로 서비스 라우팅,로드 균형 조정, 서비스 검색 및 서비스 구성 데이터 업데이트)를 지원해야합니다.

 플랫폼 자동화 : 마이크로 서비스의 런타임 확장 성과 적응성 이점을 실현하려면 외부 아키텍처에 기본 플랫폼과 관련된 자동화 기능 지원이 포함되어야합니다. 여기에는 VM 또는 컨테이너의 프로비저닝 및 각 마이크로 서비스의 실행중인 인스턴스 관리가 포함됩니다.

 모니터링 및 경고 : 분산 환경은 환경에서 문제가 발생할 때 모니터링 및 경고의 복잡성을 증가시킵니다. 외부 아키텍처는 일관되고 효율적으로이를 수행 할 수있는 기능을 제공해야합니다.

 로깅 및 진단 : 분산 환경에서 문제가 발생하면 근본 원인을 식별하고 격리하기가 어려울 수 있습니다. 분산 된 프로세스의 추적을 포함하여 자세한 계측 및 진단 분석을 지원함으로써 외부 아키텍처는 마이크로 서비스 팀이 문제를보다 신속하게 분류하고 해결할 수 있도록 지원합니다.

 ID 제공 : 토큰 기반 서비스를 사용하여 인증 및 권한 부여를 외부화하는 것은 안전한 고객 대 서비스 및 서비스 대 서비스 상호 작용을 보장하는 것이 좋습니다.

 

Client 편

마이크로서비스에 접근할 수 있는 API는 Web, Mobile, Speech, Services, Partners, IoT 등 다양합니다.

Client는 이런 다양한 형태의 API 서비스로 호출할 수 있으며 이를 통합하여 처리하는 서비스가 API Gateway입니다.

ⓐ API Gateway는 다음과 같은 형태의 Mediation을 제공합니다.

 

[그림 2] A Comparison of API Mediation Technologies @ 2018 Gartner, Inc.

 

위 이미지는 Consumer API vs Inner API간의 Mediation 그리고 Service to Service 간의 Mediation을 설명하고 있습니다.

Consumer API vs Inner API간의 Mediation은 모니터링, 보안, 트래픽 관리, 경량 통합 및 수익 창출 기능이 포함됩니다.

Service to Service 간의 Mediation은 분산 구성 관리, 동적 검색, 요청 라우팅, 로드 균형 조정, 모니터링, 자가 치유 복원 서비스, 인증 및 권한 부여 및 메시지 암호화가 포함됩니다.

 

Enterprise Gateway는 서비스 주변 진입 점에서 방어의 첫 번째 라인을 제공하고, API 소비자 사이의 트래픽을 관리하고 백엔드 중앙 집중식 모델을 사용합니다. 엔터프라이즈 게이트웨이의 예로는 Google Apigee 및 CA API 관리가 있습니다.

microgateway는 API를 엔드 포인트에서 last-mile 방어를 제공하기 위해 분산 모델을 사용합니다. 서비스가 어플리케이션 서버에 전개 될 때 API 소비자와 백엔드 서비스와 서비스사이의 트래픽을 관리합니다. microgateway의 예로 Kong과 Tyk가 있습니다.

Service Mesh는 클러스터 컨테이너 시스템에 배치 miniservices 및 microservices에 대한 서비스 간 통신을 관리하는 분산 모델을 사용합니다. 서비스 메쉬 기술의 예로는 Istio와 Linkerd가 있습니다.

 

ⓑ Service Mesh는 사실상 마이크로서비스의 핵심이라고 볼 수 있습니다.

 

[그림 3] Service Mesh Capabilities @ 2018 Gartner, Inc.

 

Service Mesh는 Miniservice 및 Microservice 환경 내에서 보안, 확장성 및 가용성을 향상시킵니다.

Service Discovery는 동적 발견을 지원하고 트래픽을 가로 채고 정책을 적용함으로써 마이크로 서비스 간의 경량 조정을 제공합니다.

현재 Service Mesh에는 Kerbernetes, PaaS (Service Platform as a PaaS) 또는 유사 탄력적으로 확장 가능한 환경과 같은 관리 컨테이너 클러스터 내의 Miniservice 및 Microservice에 대한 서비스 간 통신 관리가 있습니다.

다만, Service Mesh 기술은 비교적 새롭고 미성숙하는 점과 많은 주류 기업은 서비스 메시 기술에 대한 경험이 없으며 필요한 전문 지식이 유기적으로 개발하기 쉽지 않은지 확실하지 않다는 문제가 있습니다.

 

[그림 4] A Spectrum of Service Mesh Technologies Based on Code Coupling @ 2018 Gartner, Inc.

 

무엇보다 큰 문제는 Lock-in 이슈입니다. Service Mesh는 특히 프레임 워크 또는 라이브러리 기반 솔루션을 사용할 때 lock-in을 생성 할 수 있습니다. Linkerd 또는 Istio와 같은 사이드 카 패턴을 사용하여 배치되는 서비스 메시는 lock-in을 감소시키기 때문에 좋습니다.

사실상 Service Mesh를 대체할 수 있는 기술이 없기 때문에 마이크로서비스를 접하는 모든 IT인이 다루어야 할 기술입니다.

Service Mesh에 대한 설명이 길어졌지만, 결국 API Gateway를 통해 Service Mesh로 전달되면, Service Router를 통해 내부 로드밸러싱을 수행합니다.

이후에는 기존 레가시 서비스와 같이 서비스가 배포 된 Container에서 API를 처리합니다.

이때 성능상의 문제가 생겨서 Container가 또는 Instance가 다운되었을 경우 또는 신규로 생성되었을 경우 Automation 할 수 있는 Service Discovery의 역할을 수행합니다.

 

Operator 편

운영자는 관리자 계정, 그리고 모니터링, 로깅, 트랜잭션의 관리 조금 더 나아가서 config 관리 및 사용자 관리 수준까지 접근하게 됩니다.

ⓐ Consumer Identity Provider를 통해 관리자 인증을 승인합니다.

ⓑ Telemetry를 통해 Monitoring, Logging, Tracing을 관리하고 제어합니다.

사실상 마이크로서비스를 전환하기위한 요건의 weakness 중 하나인 Monitoring & Diagnostics에 대한 방안을 제시하는 Telemetry입니다.

 

[그림 5] Monitoring Microservice-Based Applications @ 2018 Gartner, Inc.

 

Business Transaction Tracking을 어떻게 접근해 나갈 것인가에 대한 고민과 Log를 어떻게 분석해 나갈 것인가에 대한 커다란 문제를 가지고 가지고 접근하게 됩니다. API Gateway이 갖추고 있는 서비스를 연결해 주는 트랜잭션 기능과 sleuth & brave와 같은 application dependency가 추가 되는 방법 중 선택해야 하며, 이중 어떤 기준으로 모니터링하고 로그에 쌓으며 어떠한 로그를 뽑아 낼 것인가에 대한 ELK, EFK등의 추적 방식을 결정해야 합니다.

ⓒ 그밖에 configMap과 같은 configuration을 관리하여 이미지를 재생성하지 않고 컨피그 저장소에 업데이 후 참조하는 방식으로 관리할 수 있는 Service Mesh를 사용합니다.

 마지막으로 사용자를 관리하는 Identity Provider를 관리하는 역할을 수행합니다.

 

개발자 편

개발자는 가장 단순한 프로세스를 갖고 있습니다. CI/CD에 대한 설계가 나오면 생성 / 배포 하면 끝이기 때문이죠.

ⓐ 먼저 CI/CD Automation을 위한 Build Automation, Image Repository, Deploy Automation을 구축합니다.

다양한 구축 관련 툴을 제시할 수 있지만, 일단 github, jenkins에 대한 범 공용성이 뛰어나니 그 이상의 이야기는 이후 CI/CD를 풀어 낼때 해보도록 하겠습니다.

ⓑ 생성된 이미지를 직접 Instance에 배포하거나 LoadBalancer를 통해 배포하면 모든 과정이 완료됩니다.

 

이번 포스팅에서는 MSA가 진행될때 운영자, 개발자, Client는 각각 어떠한 마이크로서비스를 타고 수행되는지에 대해 알아보았습니다.

- 출처: https://waspro.tistory.com/432

 

[MSA 개념 정립하기] MSA 아키텍처 패턴 (Client, 운영자, 개발자 측면의 흐름)

본 포스팅에서는 MSA에 접근하는 Client, 운영자, 개발자측면의 흐름도를 살펴보도록 하겠습니다. Microservice는 SOA(Service Oriented Architecture)의 경량화 버전으로 (Service: 특정 기능의 집합, service의..

waspro.tistory.com


진짜 진짜 이해하기 쉽고 좋은 포스트라고 생각합니다. MSA를 많이 접하지 않으신 분도 차근차근 읽어보시면 전부는 아니더라도 이해에 많은 도움이 됩니다. 또 MSA를 적용하셨던 분도 놓치고 있는 것이 없는지 확인이 되는 바이블 수준이네요.

@Entity

@Entity 어노테이션은 데이타베이스의 테이블과 일대일로 매칭되는 객체 단위이며 Entity 객체의 인스턴스 하나가 테이블에서 하나의 레코드 값을 의미합니다. 그래서 객체의 인스턴스를 구분하기 위한 유일한 키값을 가지는데 이것은 테이블 상의 Primary Key 와 같은 의미를 가지며 @Id 어노테이션으로 표기 됩니다.

먼저 Spring Boot 를 설정할때 spring.jpa.hibernate.ddl-auto 설정이 create 혹은 update 로 되어 있을 경우 Spring 프로젝트가 시작될때 EntityManager 가 자동으로 DDL 을 수행해 테이블을 생성해 줍니다.

이때 명시적으로 @Table  name 속성을 이용해 데이타베이스상의 실제 테이블 명칭을 지정하지 않는다면 Entity 클래스의 이름 그대로 CamelCase 를 유지한채 테이블이 생성이 되기 때문에 테이블 이름을 명시적으로 작성하는 것이 관례입니다. 왜냐하면 데이타베이스상에서 보편적으로 사용 되는 명명법은 UnderScore 가 원칙이기 때문입니다. 

@Entity
@Table(name = "ORGANIZATION")
public class Organization { 
    ...
}

@Entity
@Table(name = "EMPLOYEE")
public class Employee { 
    ...
}

@Entity
@Table(name = "EMPLOYEE_ADDRESS")
public class EmployeeAddress { 
    ...
}

 

@Column

@Column 어노테이션은 데이타베이스의 테이블에 있는 컬럼과 동일하게 1:1로 매칭되기 때문에 Entity 클래스안에 내부변수로 정의 됩니다. 만약 테이블에 a, b, c 컬럼이 있다면 각각 3개의 @Column 어노테이션을 작성 하게 됩니다. 다만 이때 의도적으로 필요없는 컬럼들은 작성하지 않아도 되는데 데이타베이스 테이블에 실제 a, b, c, d 총 4개의 컬럼이 있더라도 a,b,c 컬럼만 Entity 클래스에 작성해도 무방 하다는 이야기 입니다.

이때 @Column 어노테이션은 별다른 옵션을 설정하지 않는다면 생략이 가능합니다. 즉 Entity 클래스에 정의된 모든 내부변수는기본적으로 @Column 어노테이션을 포함한다고 볼 수 있습니다.

Spring Boot 의 spring.jpa.hibernate.ddl-auto 설정이 create 혹은 update 로 되어 있을 경우 create 일때는 최초에 한번 컬럼이 생성이 되고, update 일때는 Entity 클래스에 있지만 해당 테이블에 존재하지 않는 컬럼을 추가로 생성해 줍니다. 하지만 컬럼의 데이타 타입이 변경 되었거나 길이가 변경 되었을때 자동으로 데이타베이스에 반영을 해주지는 않기 때문에 속성이 변경되면 기존 테이블을 drop 후 새롭게 create 하던지 개별로 alter table 을 통해 직접 ddl 구문을 적용하는 것이 좋습니다.

spring.jpa.hibernate.ddl-auto 설정이 create-drop 로 되어 있으면 프로젝트가 시작될때 자동으로 기존 테이블을 drop 한 후 create 를 해줍니다. 하지만 기존 스키마가 전부 삭제 되기 때문에 시스템 설계와 개발 시점에만 사용해야 하며 운영 시점에 create, update, create-drop 을 사용 하지 않아야 합니다.

@Column  @Entity 어노테이션과 동일하게 name 속성을 명시하지 않으면 Entity 클래스에 정의한 컬럼 변수의 이름으로 생성이 됩니다. 그렇기 때문에 CamelCase 로 작성된 컬럼 변수가 있다면 UnderScore 형식으로 name 을 명시적으로 작성 합니다.

데이타베이스상에서 컬럼은 실제 데이타가 가질 수 있는 최대 길이를 가지게 되는데 이것은 데이타베이스에 데이타를 효율적으로 관리하기 위해서 입니다. @Column 에도 이처럼 length 속성으로 길이를 명시 할 수 있습니다. 만약 length 속성이 없다면 기본 길이인 255가 지정 됩니다. 이것은 문자열 형태인 데이타 속성에만 해당 되며 큰 숫자를 표현하는 BigDecimal 일 경우 precision, scale 로 최대 길이를 지정 할 수 있습니다.

@Column 
private String code;

@Column(length = 100)
private String name;

//@Column 은 생략이 가능합니다.
private String description; 

@Column(precision = 11, scale = 2)
private BigDecimal amount;

@Column
private Integer size;

@Column(name ="register_date")
private LocalDateTime registerDate;

 

@Id

데이타베이스의 테이블은 기본적으로 유일한 값을 가집니다. 그것을 PK (Primary Key) 라고 하는데 데이타베이스는 이 유일한 키값을 기준으로 질의한 데이타를 추출해 결과셋으로 반환해 줍니다. 테이블 상에 PK 가 없는 테이블도 있지만 대부분의 경우 반드시 PK 가 존재합니다.

JPA 에서도 Entity 클래스 상에 해당 PK 를 명시적으로 표시를 해야 되는데 그것을 @Id 어노테이션을 이용해 이것이 PK 임을 지정 합니다.

만약 Spring Boot 의 spring.jpa.hibernate.ddl-auto 속성이 create 로 되어 있고 아직 해당 테이블이 데이타베이스상에 존재하지 않는다면 EntityManager 가 DDL 을 통해 테이블을 생성하면서 PK 를 같이 생성해 줍니다.

@Id
@Column
private String code;

 

@GeneratedValue

PK 컬럼의 데이타 형식은 정해져 있지는 않으나 구분이 가능한 유일한 값을 가지고 있어야 하고 데이타 경합으로 인해 발생되는 데드락 같은 현상을 방지 하기 위해 대부분 BigInterger 즉 Java 의 Long 을 주로 사용합니다.

물론 String 형태의 고정된 키값을 직접 생성해서 관리하기도 합니다. 중요한 것은 대량의 요청이 유입 되더라도 중복과 deadlock 데드락이 발생 되지 않을 만큼 키값이 빨리 생성이 되고 안전하게 관리 되어야 한다는 점입니다.

deadlock

동일한 시점에 요청이 유입 되었을때 데이타베이스는 테이블 혹은 레코드를 lock 을 걸어 데이타가 변경되지 않도록 막아 놓고 다른 작업을 진행합니다.

이때 1번째 요청이 A 테이블의 값을 변경하고 lock 을 걸어둔 상태에서 B 테이블을 사용하려고 하고, 2번째 요청이 B 테이블의 값을 변경하고 lock 을 걸어둔 상태에서 A 테이블을 사용하려고 할때 데이타베이스는 우선순위를 판단 할 수 없어 그대로 교착상태에 빠져 버립니다.

이때는 어쩔 수 없이 강제로 시스템을 재시작하여 데이타베이스 커낵션을 초기화 시켜줘야 합니다.

 

가장 보편적으로 사용이 되는 데이타베이스인 MySQL, ORACLE 에는 Long 타입의 키값을 생성하는 방식이 서로 조금 다른데요. MySQL 은 auto increment 방식을 ORACLE 은 sequence 방식을 사용합니다.

- auto increment

먼저 MySQL 의 auto increment 방식은 숫자형의 PK 컬럼 속성을 auto increment 로 지정하면 자동으로 새로운 레코드가 생성이 될때마다 마지막 PK 값에서 자동으로 +1 을 해주는 방식입니다. 이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.IDENTITY 로 지정해 auto increment 컬럼인 것을 EntityManager 에 알려 줍니다.

이때 자동으로 생성되는 값을 가지는 PK 컬럼의 이름은 명시적으로 id 로 지정하는 것이 관례 입니다.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

- sequence

ORACLE 에서 사용되는 sequence 방식은 sequence ORACLE 객체를 생성해 두고 해당 sequence 를 호출할때마다 기존 값의 +1 이 된 값을 반환해 주는 방식입니다. 이를 위해 @GeneratedValue 어노테이션의 strategy 속성을 GenerationType.SEQUENCE 로 지정해 sequence 를 사용해 PK 값을 사용하겠다고 지정합니다.

@Id
@SequenceGenerator(name="seq", sequenceName="jpa_sequence")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
private Long id;

 

@EmbeddedId

앞서 데이타베이스의 테이블은 기본적으로 유일한 값을 가지는데 그것을 PK 라고 이야기 한다고 설명 드렸습니다. 일반적인 경우에는 PK 를 단일 @Id 로 구성하지만 경우에 따라선 복합키로서 테이블의 PK 를 정의 하기도 합니다. 복합키는 두개 이상의 @Id 로 구성이 되는데 이를 직접 Entity 에 정의하는 것이 아니라 별도의 Value 를 사용해 복합키를 정의합니다.

먼저 Value 를 생성한 다음 @Embeddable 어노테이션을 이용해 이 Value 가 Entity 에 삽입이 가능함을 명시 하고 Entity 에서는 @EmbeddedId 어노테이션을 이용해 이 Entity 에 해당 Value  PK 로 사용한다고 지정합니다.

@Embeddable
public class CompanyOrganizationKey implements Serializable {
	@Column(name = "company_code")
    private String companyCode;
    
    @Column(name = "organization_code")
    private String organizationCode;
}

@Entity(name = "company_organization")
public class CompanyOrganization {
    @EmbeddedId
    protected CompanyOrganizationKey companyOrganizationKey;
}

 

@Enumerated

@Enumerated 어노테이션은 java 의 enum 형태로 되어 있는 미리 정의되어 있는 코드 값이나 구분값을 데이타 타입으로 사용하고자 할때 사용됩니다. 속성으로 EnumType.ORDINAL, EnumType.STRING 이 있는데 ORDINAL 은 enum 객체에 정의된 순서가 컬럼의 값으로 사용되고 STRING 은 enum 의 문자열 자체가 컬럼의 값으로 사용이 됩니다.

enum FlagYN {
    Y, N
}

@Enumerated(EnumType.ORDINAL)
@Column(name = "access_yn")
private FlagYN accessYn; //0, 1 이 값으로 저장

@Enumerated(EnumType.STRING)
@Column(name = "use_yn", length = 1)
private FlagYN useYn; //'Y', 'N' 이 값으로 저장

 

@Transient

만약 Entity 객체에 속성으로서 지정되어 있지만 데이타베이스상에 필요없는 속성이라면 @Transient 어노테이션을 이용해서 해당 속성을 데이타베이스에서 이용하지 않겠다 라고 정의합니다. 이렇게 하면 해당 속성을 Entity 객체에 임시로 값을 담는 용도로 사용이 가능해 집니다.

@Transient
private String tempValue;

출처 : https://jogeum.net/6


사실 마지막 2개는 잘 쓰지 않았던거 같다. Entity에 Enum을 사용하는 것은 application과 DB간의 변환과정이 필요해서 많이 꺼려한 내용이었다. 그러나 @Enumerated을 몰랐던 것이였다... 보통 아래와 같이 Entity에 Enum을 쓰지 않고 String으로 사용했다.

@ColumnDefault("SEND")
@Column(name = "STATE", nullable = false, length = 10)
private String state;

NotificationMessage.builder()
  .messageId(messageId)
  .notification(notification)
  .sendDate(Instant.now())
  .state(STATE.SEND.name()).build());

이럴땐 @Enumerated(EnumType.STRING)를 사용하면 깔끔하게 해결될 것 같다.

그런데 @Transient는 조금 회의적이다. Entity에 과연 임시로 값을 담아야 하나?라는 생각은 의문이 들기 때문이다. 나는 Entity와 DTO의 구분을 지어줘야 한다고 생각하기 때문이다. 어쨌든 enum은 더 나이스하게 사용 가능 할 것 같다.

'Programming > JPA' 카테고리의 다른 글

N + 1 문제 해결 1  (0) 2020.06.11
N + 1 문제 원인  (0) 2020.06.05
왜 JPA를 써야할까?  (0) 2020.06.05
jpa 복합키에서 auto increment  (0) 2019.06.27
JPA 기본 키 전략  (0) 2019.06.27

micro service간의 통합을 위해 queue를 적극적으로 사용하려고 하면서 system 외부로 부터의 request는 어떻게 처리해야 할까 고민을 하게 되었다. 그리고 결국 AWS APIGateway를 사용하기로 결정이 났다. 사실 처음에는 system 외부 다른 서버들이 바로 SQS SDK를 사용하여 통신하면 될 것인데 SDK의 dependency도 제거 하자는 의견이었다. (개인적은 생각으로는 AWS SDK가 대다수의 많은 platform을 커버하고 있고 SQS 자체도 http를 지원하고 있기 때문에 API를 앞에 두는게 무슨 의미가 있나?라고 생각 했지만 REST로 구현 한다면 client들의 플랫폼별 구현은 통일 되는것은 분명해 보인다.)

일단 API server로는 AWS API gateway -> SQS -> service 이런 식으로 구성하였고 각 micro service들은 SQS를 통해 메세지를 받아 처리하는 식으로 추가될 것이다. 경우에 따라서는 SQS가 안 붙을 수 있겠지만 결국 API gateway를 통해서 외부 request를 처리하도록 하였다.

그림과 같이 마이크로 서비스 앞에 SQS를 적용하면서 API gateway와 연동 되어야 했는데 같은 AWS 서비스니까 클릭만으로 될꺼야...라고 넘겨짚은 것이 화근이었다. 분명 다음 그림 같이 API method에는 AWS service의 SQS를 고르는게 있어서 다 된 줄 알았다.

오! SQS 고르기만 하면 되나 보네!?

역시 AWS!! 라고 속으로 좋아하는 순간 test 결과는?

역시 꽁짜는 없었다...

error message가 "access to the resource is denied."... Execution role을 다시 봤지만 문제 없었다... 뭐가 문제지???하며 보내는 쪽이 문제 없다면 받는쪽은? 하는 생각이 들었고 SQS의 spec을 뒤지기 시작함.

금방 관련 내용을 찾아 내었다. https://docs.aws.amazon.com/ko_kr/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-making-api-requests.html

내용인 즉슨... 아래와 같음...

아... form-urlencoded로 보내야 되는거네... 그런데 API gateway를 쓰는데 어떻게 변환해??? 가능해??? 당황하고 있을 때 역시 이 내용도 정리된 내용이 있었다.

http://hunrik.me/posts/put-message-straight-into-sqs

 

How to put request straight into SQS

You dont need any fancy lambda to post into SQS

hunrik.me

감사해요 hunrik님~ 다음은 Integration Request에서 바꾸는 내용이다.

보낼 때 위 그림같이 header와 body를 변환해서 SQS API대로 넣어준다. 결론은?

해피엔딩~ ^^

먼저 중요 패키지 버전을 보면 아래와 같다.

  • springboot 2.1.5.RELEASE
    • spring-data 2.1.5.RELEASE
  • spring-core 5.1.7.RELEASE
  • hibernate-core 5.3.10.Final
  • mysql 5.7.21-log

entity는 아래와 같습니다.

@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "T_NOTIFICATION")
public class Notification {

    @EmbeddedId
    private NotificationId id;

    @CreatedDate
    @Column(name = "CREATE_DT", nullable = false, updatable = false, columnDefinition = "datetime(3)")
    private Instant createdDate;

    @LastModifiedDate
    @Column(name = "RECEIVER_RESPONSE_DT", nullable = true, updatable = false, columnDefinition = "datetime(3)")
    private Instant receiverResponseDate;

    @Column(name = "NOTI_REASON", nullable = false, length = 64)
    private String notiReason;

    @ColumnDefault("1")
    @Column(name = "SEND_COUNT", nullable = false, columnDefinition = "tinyint")
    private int sendCount;

    @Column(name = "REQUEST_USER", nullable = false, length = 36)
    private String requestUser;

    @Column(name = "SEND_MESSAGE", nullable = false, columnDefinition = "text")
    private String message;

    @Builder.Default
    @OneToMany(mappedBy = "notification", fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval = true)
    private Collection<NotificationMessage> notificationMessages = new ArrayList<NotificationMessage>();
}
@Embeddable
@AllArgsConstructor
@NoArgsConstructor
@Data
public class NotificationId implements Serializable {

    private static final long serialVersionUID = 1L;

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "NOTI_SEQ", unique = true, updatable = false, nullable = false)
    private BigInteger NotiSeq;

    @Column(name = "PROJECT_ID", nullable = false, length = 36)
    private String projectId;

    @Column(name = "RECEIVER_ID", nullable = false, length = 128)
    private String receiverId;

    @Column(name = "NOTI_GID", nullable = false, length = 128)
    private String notiId;

}
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "T_NOTIFICATION_MESSAGE")
public class NotificationMessage {

    @Id
    @Column(name = "MESSAGE_ID", nullable = false, length = 100)
    private String messageId;

    @ColumnDefault("SEND")
    @Column(name = "STATE", nullable = false, length = 10)
    private String state;

    @CreatedDate
    @Column(name = "SEND_DT", nullable = false, columnDefinition = "datetime(3)")
    private Instant sendDate;

    @Column(name = "END_DT", nullable = true, columnDefinition = "datetime(3)")
    private Instant endDate;

    @Column(name = "SEND_RESULT_CODE", nullable = true, columnDefinition = "smallint(5)")
    private Integer resultCode;

    @Column(name = "SEND_RESULT_REASON", nullable = true, length = 1024)
    private String resultReason;

    @Column(name = "SEND_RESULT_MESSAGE", nullable = true, columnDefinition = "text")
    private String resultMessage;

    @ManyToOne
    @JoinColumns(value = {
            @JoinColumn(name = "NOTI_SEQ", referencedColumnName = "NOTI_SEQ"),
            @JoinColumn(name = "PROJECT_ID", referencedColumnName = "PROJECT_ID"),
            @JoinColumn(name = "RECEIVER_ID", referencedColumnName = "RECEIVER_ID"),
            @JoinColumn(name = "NOTI_GID", referencedColumnName = "NOTI_GID"),
    }, foreignKey = @ForeignKey(name = "FK_T_NOTIFICATION_TO_T_NOTIFICATION_MESSAGE"))
    private Notification notification;
}

Notification class에 EmbeddedId를 설정하여 복합키를 사용하고 있고 NotificationId class에 정의된 복합키중에 notiSeq는 @GeneratedValue로 설정하여 auto increment를 사용하려고 하였습니다. 실제 코드에서는 

@Override
public Notification insertOrUpdatePushMessage(Message message) {
    Notification notification = convertMessageToNotification(message);
    return this.repository.saveAndFlush(notification);
}

Message를 받아서 Notification으로 변경해서 save()를 하죠. 그런데 문제는 save()하고 return된 Notification 객체에는 notiSeq가 Null이 들어오는 것이었습니다.

사실 hibernate의 batch를 사용하고 있어서 auto increment를 가져오는 타이밍이 persistant와 안 맞나 보다 하고 batch도 사용하지 않아 봤는데 실패!!! 그럼 flush를 안 해서 그런가??? 하고 위 코드처럼 saveAndFlush()로 바꿔도 실패!!! 멘붕 중에 아래 블로그를 찾았다.

https://kihoonkim.github.io/2017/01/27/JPA(Java%20ORM)/3.%20JPA-%EC%97%94%ED%8B%B0%ED%8B%B0%20%EB%A7%A4%ED%95%91/

 

(JPA - 3) 엔티티 매핑

앞에서 영속성 컨텍스트에 객체(엔티티)를 저장하는 것을 보았다.객체와 관계형 데이터베이스의 다른 개념을 어떻게 매핑하고 데이터가 반영되는지 알아보자. 객체(엔티티)를 영속성 컨테스트에 저장(persist) 후 트랜젝션이 커밋되는 시점에각 데이터베이스에 맞는 SQL을 생성하여 데이터베이스로 보내진다.객체를 어떻게 엔티티로 만들고 테이블과 매핑하는지 알아보

kihoonkim.github.io

두 방식 중 무엇을 사용하든 복합키에는 @GeneratedValue를 통해 기본키를 자동생성할 수 없다.
반드시 직접 할당 전략을 사용해야 된다.

오!!! 복합키는 안 되는 거구나.... 허무함과 안도감이 교차했다. 나중에 왜 그런지 hibernate문서를 좀 더 찾아봐야겠지만 일단 믿고 가기로 했다. 

결론은 복합키에 unique한 auto increment를 사용하는 게 모순이라고 판단해서 auto increment만 PK로 변경해서 진행 중이다.

'Programming > JPA' 카테고리의 다른 글

N + 1 문제 해결 1  (0) 2020.06.11
N + 1 문제 원인  (0) 2020.06.05
왜 JPA를 써야할까?  (0) 2020.06.05
JPA 기본 Annotation 정리  (7) 2019.07.04
JPA 기본 키 전략  (0) 2019.06.27

+ Recent posts