마이크로 프론트 엔드란 백엔드에서 사용하고 있는 마이크로 서비스 아키텍쳐 처럼 프론트엔드에서 관리하는 서비스를 분리해서 개발, 관리하는 패턴을 말한다.
마이크로 서비스 아키텍쳐가 왜 부상했는지 생각해볼 필요가 있다. 해당 아키텍쳐는 백엔드 서비스를 기준으로 설명하겠다. 먼저 마이크로 서비스 아키텍쳐의 떡상 그 배후에는 서비스의 몸집이 커지는 상황, 클라우드(aws),컨테이너(docker)등 서버 서비스의 발전이 있다. 그럼 기존에 사용하던 아키텍쳐에 어떤 점을 보완하고 나온 걸까.
기존의 방식은 모놀리식(Monilithic)이라고 한다. 하나의 큰 서비스안에서 모듈별로 개발을 하고, 그 결과물을 빌드해서 하나의 패키징하는 구조이다.
이런 구조의 서비스는 구조가 단순하다. User Interface / Business Logic / DB 이런 식으로 깔끔하게 구분하여 개발이 가능하고, 하나의 서비스이기 때문에 개발 환경의 설정과 배포가 간편한 장점이 있다. 하지만 마냥 좋은 점만 있는 것은 아니다.
이런 단점을 보완하고자 나온 아키텍쳐 중 하나가 Micro Service Architecture이다. 일명 MSA는 하나의 큰 서비스를 작은 단위의 서비스로 나누고, 서비스간의 종속성을 낮춰 독립적이고 느슨하게 결합시키는 아키텍쳐이다.
여기서 중요한건 독립적, 느슨한 결합 이라는 특징이다.
자 여기서 이제 5번 장점이 컨테이너와 클라우드서비스의 발전과 맞물린다.
예를 들어서 게임 정보 사이트가 있다고 생각해보자. 이 사이트의 서비스에는
이런 사이트를 운영하는데 게임이 대규모 업데이트를 한다고 예고하고 업데이트를 진행했다. 이 사이트를 운영하든 B모씨는 업데이트 내용을 잘 정리해서 업데이트 정보 관리 서비스에 이쁘게 올려놨다. 그럼 앞으로 어떤 상황이 벌어질까.
물론 이런 사이트를 운영하는게 B모 씨 뿐은 아니겠지만 만약 독점으로 운영하고 있다고 한다면?
바로 페이지 터지는 거다. 왜냐 B모씨의 모놀리식 패턴으로 만들어진 서비스는 클라우드 서버에 배포되서 운영되는데, 아니 사람들이 대규모 업데이트라고 몰린 것이다. 그러면 이제 놀란 B모씨는 물 들어 올 때 노 젓자는 마음으로 서버를 증설한다.
몇 개의 서버컴을 aws ec2를 이용해서 결제하고 전체 서비스를 각각 올린다. 그런데 한 1주일이 지나고 게임 뽕이 빠진 사람들이 사이트 접속을 안하니 트래픽 발생이 사건 발생의 20분의 1로 줄었다. 그러면 다시 전체 서비스가 올라간 ec2를 꺼야한다.
여기서 생각 해볼 점은 ”전체 서비스가 올라간다” 는 점이다. 사용하는 용량이 크면 돈을 더 많이 내는 클라우드 성격상 전체 서비스를 올린 B모씨는 필요한 서비스만 확장하는 것 보다 더 많은 돈을 aws에게 뜯겼을 것이다. 하지만 B모씨가 더 열심히 공부해서 MSA를 알았다면, 이런 일이 발생했을 때, 2번 정보 서비스, 3번 업데이트 정보 관리 서비스가 배포된 서버만 증설하면 됐을 것이다. 결국 돈이 문제다.
이렇게 클라우드, 컨테이너의 발전으로 서비스를 분리해서 운영할 때 발생하는 이점이 점점 커지고 있기 때문에 위에서 보는 4번 장점이 더 두드러질 수 있다.
이렇게 만 보면 완전 무안단물 같은 MSA인데 모든 것엔 장,단이 있기 마련이다. MSA의 단점으로는
하지만 이런 단점들은 이제 조심조심해서 서로 잘 소통하고 하면 추웅분히 해결할 수 있지 않을 까 싶다.
마이크로 서비스는 결국 작은 서비스를 독립적으로 만들면서 이득을 취한다. 그럼 이제 독립적인 것들을 어떻게 하나의 큰 서비스에서 운영할 것인가가 관건이 된다.
각 서버별로 html을 요청해서 최종 응답서버가 결과물들을 하나로 합쳐서 내려주는 방식이다.
<html>
<head></head>
<body>
<h1>공통 템플릿</h1>
</body>
</html>
Nginx를 이용하여 이 html을 주고 URL에 따라서 Nginx경로를 바꿔준다.
server{
listen 8080;
root /usr/template/nginx/html
index index.html
# Redirect / to /browse
location /browse{
set $PAGE 'browse'
}
location /update{
set $PAGE 'update'
}
location /info{
set $PAGE 'info'
}
}
Micro Frontend를 npm같은 패키지로 배포하고 컨테이너가 되는 앱이 그걸 라이브러리 종속성으로 포함하는 방식. 근데 잘 생각해보면 이러나 모놀리식이나 거기서 거기다. 뭐 하나 변경하고 적용하려면 다시 빌드 배포 해야하니까 말이다.
그러면 생각해보자, 감싸는 껍데기(컨테이너)를 건들이지 않고 내부 업데이트를 시켜야하는 상황에 직면한 것이다. 곧 빌드 통합이 아닌 런타임에 통합이 되어야한다.
껍데기는 안건들이고 내부 콘텐츠를 바꿀 수 있는 방법이라 하면 그냥 그 서비스 페이지 불러와서 보여주는 건 어떨 까. iframe이라는 좋은 게 있는데.
그렇다 이게 제일 쉽긴하다. iframe내부 콘텐츠는 외부 콘텐츠 css,글로벌 변수 와는 독립적이게 된다. 하지만 우리는 또 다른 점을 생각해봐야하는데, iframe을 사용하게 되면 SEO라던지 라우팅, deep linking같은 부분에서 손해를 보게 된다. 또 iframe 태그안에 갇히기 때문에 스타일링이 제한적일 수 있다.
현실적으로 사용하는 방식으로 각 마이크로 앱은 script태그로 페이지에 통합된다. 컨테이터 앱은 마이크로 앱을 script 태그로 다운 받고, 공통으로 약속된 랜더 메소드를 실행한다.
<html>
<head></head>
<body>
<h1>공통 템플릿</h1>
<div id='micro-front-end'></div>
<script src='https://도메인.login.com/bundle.js'/>
<script src='https://도메인.info.com/bundle.js'/>
<script src='https://도메인.update.com/bundle.js'/>
<script>
const microFERoute = {
'/':window.renderLogin,
'/info':window.renderInfo,
'/update':window.renderUpdate,
} // 해당 전역 함수는 위에서 불러온 번들에 있는 것들
const containerRender = microRERoute[window.location.pathname];
containerRender('micro-front-end');
// 랜더링할 js를 경로에 따라 선택 후 랜더링할 엘리먼트의 id를 준다.
</script>
</body>
</html>
추가적으로 web-component를 이용할 수도 있다.
js를 통한 통합처럼 하나의 html안에서 불러와서 사용하는 방식을 사용하면 고려할 사항이 몇 가지 있다.
사내에서 백엔드 부분은 MSA를 적용해야한다는 말이 나왔을 때, 난 프론트니까 상관없겠지 라는 생각을 했지만 이젠..아니야 였다. 프론트에서의 서비스가 점점 다양하고 비대해지면 유지, 보수를 위해 MFE를 적용해야 누가 언젠가 할지 모를 프로젝트의 추가 수정 등의 유지보수를 하기 편할것이고, 그 누군가가 내가 될 가능성이 크기 때문에 더 좋은 방향으로 변화되어야 한다는 걸 느낀다.
참조
https://gruuuuu.github.io/cloud/architecture-microservice/
https://martinfowler.com/articles/micro-frontends.html#TheExampleInDetail