Skip to content
Home » 마이크로 프론트 엔드 | [Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처 상위 285개 베스트 답변

마이크로 프론트 엔드 | [Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처 상위 285개 베스트 답변

당신은 주제를 찾고 있습니까 “마이크로 프론트 엔드 – [Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처“? 다음 카테고리의 웹사이트 kk.taphoamini.com 에서 귀하의 모든 질문에 답변해 드립니다: kk.taphoamini.com/wiki. 바로 아래에서 답을 찾을 수 있습니다. 작성자 FEConf Korea 이(가) 작성한 기사에는 조회수 2,039회 및 좋아요 62개 개의 좋아요가 있습니다.

Table of Contents

마이크로 프론트 엔드 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 [Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처 – 마이크로 프론트 엔드 주제에 대한 세부정보를 참조하세요

마이크로 프론트 엔드 주제에 대한 자세한 내용은 여기를 참조하세요.

micro-frontend – velog

마이크로 프론트 엔드란 백엔드에서 사용하고 있는 마이크로 서비스 아키텍쳐 처럼 프론트엔드에서 관리하는 서비스를 분리해서 개발, 관리하는 패턴 …

+ 자세한 내용은 여기를 클릭하십시오

Source: velog.io

Date Published: 4/23/2021

View: 5558

[MEDIUM DAILY DIGEST] 마이크로 프론트엔드 MICRO …

존재한다! 여기서 마이크로 프론트엔드 아키텍쳐가 정말로 빛을 발할 수 있다. 마이크로 프론트엔드는 여러개의 독립적으로 배포가능한 프론트엔드 …

+ 여기에 자세히 보기

Source: yumyumcoding.tistory.com

Date Published: 10/3/2022

See also  상담사 되는 법 | (2020년기준)심리상담사 되는 법! 이 영상 하나로 시작부터 급여 면접까지 끝낸다. 상위 77개 답변

View: 745

Lattice로 마이크로 프론트엔드를 구축하는 법 – 요즘IT

이번 글은 과열되고 있는 OTT 서비스 시장의 영원한 강자 ‘넷플릭스(Netflix)’가 작성한 ‘Lattice로 마이크로 프론트엔드를 구축하는 법’을 번역 …

+ 여기에 보기

Source: yozm.wishket.com

Date Published: 1/15/2021

View: 6744

(설계)Micro Frontend – 흥부의 개발블로그

마이크로 프론트엔드란? 특정 기준으로 프론트엔드를 나누는 개발방식으로 프론트앱에 여러 기능과 섹션이 존재하여 통합해서 관리하기 힘들때 사용.

+ 여기에 더 보기

Source: dhdnjswo5000.tistory.com

Date Published: 6/12/2022

View: 1221

Micro-frontend React, 점진적으로 도입하기 – 토스

Day 2. Session. Micro-frontend React, 점진적으로 도입하기. 거대한 모놀리식 Django 프로젝트에 현대적인 프론트엔드 인프라를 구축한 사례를 공유합니다.

+ 자세한 내용은 여기를 클릭하십시오

Source: toss.im

Date Published: 7/12/2022

View: 8371

리액트를 사용하여 마이크로프론트엔드 개발하는 방법 – 끄적끄적

마이크로엔드들을 컨테이너 앱으로 합치는 방법은 여러가지가 있다. 이러한 방법들은 컴포지션을 사용한 서버사이드에서부터 프론트엔드 빌드 타임에서 …

+ 여기에 더 보기

Source: caramellow.tistory.com

Date Published: 5/30/2021

View: 6784

마이크로프론트엔드를 위한 6가지 패턴 개요 – 블로그 – Quish

마이크로프론트엔드는 새로운 것은 아니지만 확실히 최근의 추세입니다. 2016년에 만들어진 이 패턴은 대규모 웹 앱을 개발할 때 문제가 나타나기 시작하면서 서서히 인기 …

+ 여기에 자세히 보기

Source: ko.quish.tv

Date Published: 2/3/2022

View: 5362

주제와 관련된 이미지 마이크로 프론트 엔드

주제와 관련된 더 많은 사진을 참조하십시오 [Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

[Track 2-4] 원지혁 - 프론트엔드에서의 마이크로 서비스 아카텍처
[Track 2-4] 원지혁 – 프론트엔드에서의 마이크로 서비스 아카텍처

주제에 대한 기사 평가 마이크로 프론트 엔드

  • Author: FEConf Korea
  • Views: 조회수 2,039회
  • Likes: 좋아요 62개
  • Date Published: 2019. 11. 17.
  • Video Url link: https://www.youtube.com/watch?v=FQoNY2W0s4E

Mobile Convergence :: [Micro Frontend] 마이크로 프론트앤드

Micro frontends | Technology Radar | ThoughtWorks

This Technology Radar quadrant explores the techniques being used to develop and deliver software

www.thoughtworks.com

Micro Frontends, 글로 배워보자구요.

반응형

마이크로 프론트앤드는 마이크로 서비스의 개념을 프론트엔드 세계로 확장한 개념이다.

마이크로 서비스처럼 전체 화면을 작동할 수 있는 단위로 나누어 개발한 후 서로 조립하는 방식이다.

Main Concept

기술 독립성

각 작동 단위들은 기술적으로 독립적일 수 있어야 한다.

각 작동 단위에 사용된 프론트앤드 기술(React, Vue, Vanilla JS 등)에 상관 없이 조합이 가능해야 한다.

컨텍스트 독립성

각 작동 단위들이 같은 프레임워크를 사용하더라도, 컨텍스트를 공유해선 안된다.

독립적인 애플리케이션을 자체적으로 구축해야 하고, 상태 공유나 전역 변수에 의존해서는 안된다.

네임스페이스를 활용한 분리

각 작동 단위의 격리가 불가능한 경우, 네이밍 컨벤션에 따라 prefix 등으로 네임스페이스를 활용한다.

CSS, 로컬 스토리지, 이벤트, 쿠키에 네임스페이스를 부여하여 충돌을 방지하고 명확히 분리한다.

통신 시스템에 기본 브라우저 기능 활용

작동 단위 간의 통신을 위한 시스템을 자체 구축하는 것보다, 브라우저 이벤트를 사용한다.

만약 정말로 작동 단위 간 커스텀 API가 필요한 경우, 가능한 한 간단하게 유지한다.

탄력적인 웹 디자인 구축

자바스크립트에서 에러가 나거나 실행할 수 없더라도, 기능은 사용 가능해야 한다.

범용 렌더링(Universal Rendering)과 점진적 향상(Progressive Enhancement)을 통해 성능을 향상시킬 수 있다.

Universal Rendering

Progressive Enhancement

장점

작고, 응집력 있고 유지보수에 용이한 코드베이스를 가질 수 있다. 따라서 디커플링이라는 소프트웨어 개발 목표를 달성한다.

각 마이크로 프런트 엔드는 고유한 기술 및 프레임 워크를 선택할 수 있다. 독립적으로 구현, 테스트, 업그레이드, 업데이트 및 배포 할 수 있어, 팀에 유연성을 제공한다.

프론트앤드 개발을 점진적 업그레이드 또는 재작성이 수월해진다.

마이크로 프런트 엔드는 수직 팀을 장려한다. 수직 팀에는 일반적으로 기능 소유자, UX 디자이너, 제품 관리자, 백엔드 개발자, 프런트 엔드 개발자 및 품질 보증 엔지니어가 포함된다.

단점

마이크로 프런트 엔드는 분리되거나 느슨하게 결합 된 구성 요소를 위해 설계되었다. 그들 사이에 너무 많은 종속성을 넣으려고하면 디버깅 악몽이 발생할 수 있다.

마이크로 프런트 엔드를 가능하게하는 파이프라인으로 인해 복잡도가 증가한다. 외부로드 문제를 해결하려면 기술적 전문 지식이 필요하며 디버깅 프로세스에는 시간이 많이 걸린다. 또한 SSO (Single Sign-On), 글로벌 CSS 등과 관련된 문제에 직면 할 수 있다.

각 마이크로 프런트 엔드에는 중복 된 코드 또는 기능이 있을 수 있다. 예를 들어, React 라이브러리는 각 마이크로 프런트 엔드에 포함될 수 있어, 번들 크기와 메모리 소비를 증가시킨다.

런타임 시 마이크로 프런트 엔드를 동적 또는 지연로드하는 데 추가 시간이 걸린다.

사용자 인터페이스는 여러 팀에서 설계되었으므로 UX 설계는 마이크로 프런트 엔드에서 일관되지 않을 수 있다.

반응형

Micro Frontend 통합 방법

독립적인 개발 및 배포

마이크로 앱들을 개발 후, 어떻게 통합할지 고려해야 한다.

여러 방식이 있는데, 공통적으로 존재하는 아키텍처가 있다.

일반적으로 마이크로 애플리케이션들이 있고(작동 단위), 그것들을 구성하는 단일 컨테이너 애플리케이션이 있다.

단일 컨테이너 애플리케이션은 다음과 같은 것들을 한다.

공통 페이지 요소를 렌더링한다.

인증 및 탐색과 같은 공통적으로 고려되어야 하는 문제들을 해결한다.

다양한 마이크로 앱들을 페이지에 모으고, 각 앱들에게 언제 어디서 렌더링할지 알려준다.

서버 사이드 템플릿 통합

각 서버로 html 템플릿을 요청하고, 최종 응답서버에서 각 템플릿을 조합해서 응답을 보낸다. 서버측에서 최종 화면을 조합한다. (SSR 에 적용)

코드로 살펴보자.

아래와 같이 범용적인 페이지 요소들을 포함하는 index.html 파일을 만든다. 이 파일에는 HTML fragment 파일들이 서버사이드에서 포함되어진다(넣어진다).

Feed me

🍽 Feed me

Nginx를 사용하여 아래 파일을 제공하고, 요청되는 URL이 $PAGE 변수에 대입되도록 구성한다.

server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; ssi on; # Redirect / to /browse rewrite ^/$ http://localhost:8080/browse redirect; # Decide which HTML fragment to insert based on the URL location /browse { set $PAGE ‘browse’; } location /order { set $PAGE ‘order’; } location /profile { set $PAGE ‘profile’ } # All locations should render through index.html error_page 404 /index.html; }

위와 같은 방식은 상당히 표준적인 서버측 구성 방식이다.

여기엔 어떻게 여러 개의 HTML 파일들이 웹 서버에 배치되는지는 나와있지 않지만, 각 파일들은 다른 페이지들을 고려하거나 다른 페이지들에게 영향을 미치지 않는다고 가정한다. 또한, 각각이 독립적인 배포 파이프라인을 가진다고 가정한다.

독립성을 높이기 위해, 각 마이크로 앱들을 렌더링하고 서비스하는 별도의 서버가 있을 수 있으며, 한 서버는 한 마이크로 앱에 요청을 한다. 또한 응답 캐싱을 통해 지연 시간에 영향을 주지 않고 작업을 수행하도록 할 수 있다.

각 서버는 독립적으로 빌드 및 배포할 수 있음

빌드타임 통합

마이크로 프론트엔드를 패키지로 배포하고, 컨테이너 앱이 그것들을 라이브러리 종속성(library dependencies)으로 포함하도록 하는 것이다.

코드로 살펴보면, 아래와 같은 package.json이 작성될 수 있다.

{ “name”: “@feed-me/container”, “version”: “1.0.0”, “description”: “A food delivery web app”, “dependencies”: { “@feed-me/browse-restaurants”: “^1.2.3”, “@feed-me/order-food”: “^4.5.6”, “@feed-me/user-profile”: “^7.8.9” } }

이 방식은 배포 가능한 단일 Javascript 번들을 생성한다. 그리고 다양한 공통 종속성을 제거할 수 있다.

하지만 이 접근 방식은 각각의 마이크로 앱에 변경사항이 있을 때마다, 그 변경사항을 반영한 단일 번들을 만들기 위해 다시 컴파일하고 릴리즈해야 한다.

애플리케이션을 개별 코드베이스로 나누는 많은 문제들을 해결해놓고, 다시 릴리즈 단계에서 커플링하는 방식은 제외하자. 마이크로 프론트엔드를 빌드타임이 아닌 런타임에 통합하는 방법을 찾아야 한다.

iframe을 통한 런타임 통합

전통적인 방식이면서 가장 쉬운 방식이다.

iframe을 사용하면 각 마이크로 페이지들을 독립적인 하위 페이지로 쉽게 만들 수 있다. 스타일과 글로벌 변수가 서로 간섭하지 않는다는 측면에서 상당한 수준의 고립성을 제공한다.

Feed me!

Welcome to Feed me!

새로운 기술이 아니라서 흥미롭지 않을 수 있지만, 마이크로 프론트엔드의 이점을 상기해본다면, 애플리케이션 분할과 팀을 구조화하는 데에 신경쓴다면, 이 방식이 가장 적합하다.

Iframe 방식을 선택하는 것을 꺼리는 경우가 종종 있는데, 사실 그런 데에는 타당한 이유가 있다. 이 방식을 통한 분리는 쉽지만, 다른 옵션들보다 유연성이 떨어지는 경향이 있다.

이 방식은 어플리케이션의 서로 다른 부분들을 통합 구축하는 것이 어렵다. 따라서 라우팅, 히스토리, deep linking이 더욱 복잡해지고, 반응형 페이지 개발에도 추가적인 어려움들이 따른다.

UX가 iframe안에 갇히기 때문에 어색한 UI 표현을 가질 수 있다.

Javascript를 통한 런타임 통합

iframe과 달리 유연한 통합이 가능하다. 현실적으로 가장 많이 사용하는 방식이다.

각 마이크로 앱들은

위 예시는 굉장히 기초적인 예시지만, 그래도 기본적인 기술을 보여준다.

빌드타임 통합과는 달리, 각 번들.js 파일들을 독립적으로 배치할 수 있고, iframe 통합과는 달리, 원하는대로 마이크로 앱들 간의 통합이 가능하도록 유연성을 제공한다. 필요한 경우에만 번들을 다운로드하거나, 마이크로 앱이 렌더링될 때 데이터를 주고받는 등 다양한 방법으로 코드를 확장할 수 있기 때문이다.

이처럼, 이 접근 방식은 유연성과 독립적 구현 가능성이 결합되어 자주 채택된다.

Full Examples

Web Components를 통한 런타임 통합

이 방식은 자바스크립트를 통한 런타임 통합 방식에서 한 가지만 변형이 있는 방식이다.

각 마이크로 앱이 호출할 컨테이너의 전역 함수를 정의하여, 상위에서 하위 앱들의 전역 함수를 호출하는 방식이 아니다.

대신, 컨테이너 앱에서 인스턴스화할 HTML Custom Element를 정의하는 방식 이다.

Feed me!

Welcome to Feed me!

최종 결과는 이전 방식과 매우 유사하다.

주요 차이점은 '웹 컴포넌트 방식'을 선택한다는 것이다.

웹 컴포넌트 스펙을 좋아하고, 브라우저가 제공하는 기능을 사용하는 것을 좋아한다면 이 옵션이 적합하다.

컨테이너 앱과 마이크로 앱들 사이에 고유한 인터페이스를 정의하여 사용하려면, 이전 방식이 적합하다.

Mirco Frontend 통합 고려사항

스타일링 BEM과 같은 엄격한 네이밍 규칙 사용 SASS와 같은 전처리기를 통해 상위 선택자 추가 등을 통해 독립성 유지 모든 스타일을 CSS-in-JS 방식으로 처리 Shadow DOM: 메인 document DOM 으로부터 독립적으로 렌더링 되는 캡슐화된 Shadow DOM 트리를 엘리먼트에 추가하고, 연관된 기능을 제어하기 위한 JavaScript API 의 집합. 이 방법으로 엘리먼트의 기능을 프라이빗하게 유지할 수 있어, 다큐먼트의 다른 부분과의 충돌에 대한 걱정 없이 스크립트와 스타일을 작성할 수 있다.

component library 공유 마이크로 앱들 간의 시각적 일관성을 유지하기 위해 재사용 가능한 UI 컴포넌트 라이브러리를 개발하여 공유한다. 코드 재사용성과 시각적 일관성을 유지할 수 있다. 너무 일찍 만드는 것은 좋지 않다. 실제로 사용하기 전에 컴포넌트의 API가 무엇이어야 하는지 추측하기는 쉽지 않다. 그러므로 많은 변동이 발생하게 되므로, 초기에는 일부 중복이 발생하더라도 각 팀이(각 마이크로 앱 내에서) 코드베이스에서 필요에 따라 자체 컴포넌트를 만들어두는 것이 좋다. 패턴이 자연스럽게 나타나도록 허용하고, 컴포넌트의 API가 명확해지면 그 때 만드는 것을 권장한다.

애플리케이션 간 커뮤니케이션 가능한 적은 소통을 하는 것이 좋다. Custom events를 사용하면 마이크로 앱들이 간접적으로 통신할 수 있다. 이는 직접 결합을 최소화하는 좋은 방법이지만, 마이크로 앱들 간 약속을 정하고 구현해야 하므로 더 어려울 수 있다. 콜백과 데이터를 아래쪽으로 전달하는 React 모델로 약속을 명확하게 정하는 것도 좋은 솔루션이다. URL 표시 줄을 통신 메커니즘으로 사용하는 것도 좋은 방법이다. 어떤 방식을 선택하든, 서로 메시지 또는 이벤트를 전송하여 통신하는 것이 좋으며, 공유 상태를 갖는 것은 좋은 선택지가 아니다. 마이크로 서비스에서 데이터베이스를 공유하는 것이 좋지 않은 것처럼, 데이터 구조와 도메인 모델을 공유하자마자 엄청난 양의 결합이 생기게 되고, 변경에 많은 제약이 따르기 때문이다. vuex는 일반적으로 하나의 응용 프로그램에 대해 단일 전역 공유 저장소를 기본으로 한다. 그러나 각 마이크로 앱들이 자체 저장소를 갖는 것이 더 합리적이다.

백엔드 통신 BFF(Backend for Frontend Pattern) 패턴으로 프론트앤드 전용 API를 갖는다. 각 마이크로 앱들은 해당 프론트 엔드의 요구사항만 충족하는 목적을 가진 백엔드만을 가진다. 별도의 데이터베이스를 가질 수도 있다. 로그인 인증 정보는 통합하는 Container가 소유한다. 초기화 시 인증을 통해 얻은 토큰을 각 마이크로 프론트엔드에 주입되며, 마이크로 프론트엔드는 서버에 대한 요청과 함께 토큰을 보내고, 해당 마이크로 앱의 백엔드에서는 필요한 모든 유효성 검사를 수행하는 방식으로 구성할 수 있다.

프론트앤드와 백앤드의 구조화

통합 테스팅

모놀리틱과 마이크로 방식은 테스팅에서 모두 동일하지만 한 가지 명확한 차이는, 다양한 마이크로 프론트엔드와 컨테이너 애플리케이션의 통합 테스트이다. 이 작업은 기능 테스트나 e2e 테스트 도구(selenium, cypress 등)를 사용하여 수행할 수 있으나, 그렇게 멀리 갈 것 까진 없다.

통합 기능 테스트는 low level에서 다룰 수 없는 테스트만 다루면 된다. low level 에서는 비지니스 로직, 렌더링 로직 정도를 커버하는 수준의 단위 테스트를 수행하고, 통합 기능 테스트에서는 각 페이지들이 잘 조합되었는지만 확인하면 된다. 예를 들면, 특정 URL 에 완전히 통합된 애플리케이션이 로드되었는지 확인하기 위해, 마이크로 프론트엔드의 하드코딩 된 특정 텍스트가 페이지에 존재하는지를 assert 하는 것이 될 수 있다.

References

반응형

[MEDIUM DAILY DIGEST] 마이크로 프론트엔드 MICRO FRONTEND

이미지 출처: https://tomsoderlund.medium.com/micro-frontends-a-microservice-approach-to-front-end-web-development-f325ebdadc16

들어가기전..

오늘 아침에도 메일함에는 미디엄 데일리 다이제스트 메일이 와있었다. 볼만한 기사를 띄우고 하나하나 읽은 다음 어떤 걸 정리해볼까 고민했는데 마침 졸업과제로 마이크로서비스에 대한 것을 하고있기도 하고, 예전에 일했던 곳에서 bit(bit.dev https://bit.dev)를 도입하려고 공부했던 적이 있었기 때문에 마이크로 프론트엔드에 대한 포스팅을 공부하고 또 정리하려고 한다. 졸업과제 프론트엔드를 개발하면서 드롭다운이나 버튼 같은 컴포넌트들을 나만의 라이브러리로 만들어보고 싶다는 생각을 했었는데 마이크로 프론트엔드에 대해서 좀 더 잘 알고 bit를 활용한다면 충분히 가능하지 않을까 생각한다. 메일에 온 글을 정리해서 포스팅하려고 했지만 찾아보니 더 좋은 글이 있는 것 같아서 그 글을 포스팅하려고 한다.

이 글은 아래 링크의 글을 번역 및 정리한 글입니다.

https://medium.com/rocket-mortgage-technology-blog/micro-front-end-architectures-9875ca1b8b05

요약

웹이 계속해서 복잡해지면서, 프론트엔드 개발자들은 마이크로서비스 아키텍쳐가 나오기 전에 백엔드 개발자들이 겪었던 고통을 똑같이 경험하고 있다.

: 많은 도메인 바운더리에 걸쳐서 퍼져있는 큰 모놀리식 코드들과 높은 결합도, 나쁜 확장성, 느린 기능 개발, 머지 충돌의 어려움과 고도로 조직화된 개발 등..

이 기사에서는 마이크로 프론트엔드(micro front-end)라 불리는 프론트엔드 소프트웨어 아키텍쳐의 현대적인 접근을 다룰 것이다. 그리고 마이크로 프론트엔드의 코어 컨셉과, 장점, 단점에 대해서 배울 것이다.

마이크로 프론트엔드(Micro Front-End)란?

몇년 전, 하나의 모놀리식 백엔드가 여러 팀과 확장된 많은 도메인에서 작업을 하는 것은 흔한 일이었다. 이러한 단일성으로 인해, 더 집약적인 엔드포인트를 다른 덜 집약적인 것들로부터 독립적으로 확장하는 것이 불가능했다. 하나의 코드베이스에서 많은 팀이 일하기 때문에 머지 충돌에 어려움을 겪는 것이 흔한 일이었고, 배포를 성공적으로 하기 위해서는 고도의 조정이 필요했다. 이것은 상당한 시간을 필요로 했고, 느린 기능 개발을 초래했다.

마이크로서비스는 백엔드 서비스를 설계하고 구축하는 것에 대한 생각에 큰 변화를 가져올 수 있다. 하지만 프론트엔드는? 유저 인터페이스(User interfaces : UI) 와 사용자 경험 (User experience : UX) 는 해마다 점점 더 복잡해져왔다. 하지만 많은 사람들은 아직도 그들의 프론트엔드 모놀리식 코드베이스에서 벗어나지 못하고 똑같은 이슈를 겪고있다. 이에 대한 솔루션이 존재하는가?

존재한다! 여기서 마이크로 프론트엔드 아키텍쳐가 정말로 빛을 발할 수 있다. 마이크로 프론트엔드는 여러개의 독립적으로 배포가능한 프론트엔드 어플리케이션을 하나의 응집된 사용자 경험으로 구성하는 아키텍처 스타일이다. 이 용어는 2016년 11월 “ThoughtWorks Technology Radar” 에서 조직들이 필요할때 적용할 수 있는 증명된 기술로서 리스트에 올라 처음 등장하였다.

마이크로 프론트엔드를 언제 사용해야할까?

당신이 여러 개발 팀에서 작업하고 도메인 바운더리를 넘나드는 많은 기능을 포함한 아주 큰 프론트엔드 프로덕트를 만들 때 마이크로 프론트엔드가 적합하다. 마이크로 프론트엔드 아키텍쳐는 여러 업무를 담당하는 팀들이 보다 독립적이고 자율적으로 될 수 있게 돕고, 개발을 빠르게 할 수 있고, 자주 전달이 가능하게 돕는다.

하지만, 마이크로 프론트엔드는 모든 것을 만족시키는 솔루션이 아니다. 한 두팀이 운영하는 하나의 비즈니스 도메인에 포함되는 소규모 프론트엔드 프로덕트에서는 마이크로 프론트엔드가 지나칠 수 있다. 마이크로 프론트엔드 아케텍처를 사용하지 않더라도, 어플리케이션의 기능을 모듈화하여 약결합과 높은 응집성을 가져갈 수 있다. 그리고 나서 마이크로 프론트엔드에 대한 필요가 생기면, 그때 코드 베이스 일부를 마이크로 프론트엔드로 추출하는 것은 어렵지 않을 것이다.

왜 마이크로 프론트엔드 아키텍쳐를 사용할까?

마이크로 프론트엔드는 다른 모든 것들과 마찬가지로 고유한 장단점을 가지고 있다. 마이크로프론트엔드가 당신의 제품에 적합한지 판단하기 위해선 당신의 문제들과 이점들을 잘 따져보는 것이 중요하다.

장점

마이크로 프론트엔드 아키텍쳐의 가장 큰 이점은 어플리케이션과 코드와 관련이 된 것이 아니라 팀의 조직 및 프로세스와 관련된 것이다. 다음은 명시적으로 언급할 만한 10가지 주요 장점들이다.

1. 도메인 바운더리 간의 부적절한 결합을 감소

2. 각 팀의 자율성의 수준을 높임

3. 배포를 수직적으로 나누어서 독립적인 테스팅이 가능함

4. 축소된 범위에 중점을 두어 이해력이 향상

5. 더 작은 코드베이스에서 유지관리성 향상

6. 프레임워크와 dependency들에 대한 교체와 업그레이드를 더 쉽게할 수 있음

7. 레거시 코드베이스를 점진적으로 업데이트하는 능력 향상

8. 주인의식을 높일 수 있음

9. 특정 도메인에서 더 많은 주제에 대한 전문가 양성을 도움

10. 쉽게 식별할 수 있는 문제들에 대한 재사용성 촉진

단점

마이크로 프론트엔드 아키텍쳐를 사용하면 당신의 프로덕트에 추가적인 복잡함과 문제들을 도입하는 것이다. 이 문제들중 일부는 해결이 가능할 것이지만, 다른 일부는 해결할 수 없다. 다음은 가장 중요한 단점 10가지이다.

1. 몇몇 접근은 모던 브라우저에서만 호환됨

2. 하나의 마이크로 프론트엔드에서 다른 프론트엔드로 스타일이 샐 수 있는 가능성이 있음

3. 마이크로 프론트엔드에서 하나의 응집성 있는 사용자 경험을 유지하는 것의 어려움

4. 네트워크 트래픽증가가 성능저하로 이어질 수 있음

5. 시간이 갈수록 페이로드 크기가 증가하여 더 큰 다운로드가 필요

6. 중복된 dependenct가 여러번 로드되어서 낭비가 발생할 수 있음

7. 마이크로 프론트엔드 컴포넌트 간에 통신의 어려움

8. 환경 차이로 인해 "내 컴퓨터에서는 되는데" 문제가 발생할 수 있음

9. 더 많은 레포지토리, 툴, 파이프라인, 서버등의 유지해야할 것이 더 늘어남

10. 팀 간에 동일한 문제를 해결하려고 해서 더 많은 노력이 들 수 있음

(마이크로 프론트엔드 아키텍쳐를 사용하는 예시는 생략하였습니다. 이에 대해 알고 싶으시면 위의 링크를 따라가 원본 글을 확인하시길 바랍니다.)

결과

이 기사가 마이크로 프론트엔드가 무엇인지 더 잘 이해하는데 도움이 되었기를 바란다. 마이크로 프런트엔드 아키텍쳐가 합리적인 경우들에 대해서 더 잘 알고 있어야 하고 또한 마이크로 프론트엔드 패턴이 좋은 이유와 불리한 이유에 대해서도 이해해야 한다. 마지막으로 현재 업계에서 널리 사용되고 있는 최신 통합 옵션에 대해서도 잘 알고 있어야 한다.

이러한 지식을 바탕으로 제품을 위한 아키텍쳐 비전에 대해 더 나은 정보에서 비롯된 결정을 내릴 수 있다. 이 정도를 팀과 조직에 전달하여 이러한 패턴이 당신의 제품에 적합한지 논의하기 바란다.

Lattice로 마이크로 프론트엔드를 구축하는 법

해외 유명 IT 기업은 각자 자신들의 블로그를 운영해 개발 방법과 기업 문화 등을 소개하고 있습니다. 요즘IT는 이러한 IT 기업 블로그의 콘텐츠를 번역해 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하는 걸까요?

이번 글은 과열되고 있는 OTT 서비스 시장의 영원한 강자 '넷플릭스(Netflix)'가 작성한 ‘How We Build Micro Frontends With Lattice'를 번역했습니다. 넷플릭스는 어떻게 일하는 방식을 바꾸고자 하는지 궁금하시다면 아래의 글을 통해 알아보겠습니다.

넷플릭스가 꾸준히 성장하면서 레버뉴/그로스 엔지니어링[1]의 수요 또한 빠르게 진화하고 있습니다. 우리의 툴도 그만큼 빠르게 진화할 필요성이 생겼고요. 레버뉴/그로스 툴(Revenue and Growth Tools, 이하 RGT) 팀은 레버뉴/그로스 엔지니어링 관련 부서에서 언제든 쉽게 활용할 수 있는 솔루션을 마련하고자, 여러 가능성을 열어 두고 새로운 툴을 만들어 보기로 마음먹었습니다. 그 과정에서 우리는 한 가지 사실을 발견합니다. 다양한 툴에서 공통적으로 사용된 몇몇 디자인 패턴과 시스템 구성요소들로 인해 불필요한 작업이 여러 번 반복되고 있었습니다.

우리는 레버뉴/그로스 엔지니어링 소속 팀들의 동반 성장을 위해서 이 툴들을 통합할 필요가 있었습니다. 마이크로 프론트엔드[2]의 민첩함과 프레임워크[3]의 확장성을 동시에 겸비한 툴이 있다면 더 많은 사람들이 우리 툴을 사용하게 될 거라 생각했죠. 누구나 커스터마이징 할 수 있고, 확장이 쉬운 부분들만 골라서 사용자들의 세부적인 비즈니스 및 기술적인 요구사항을 충족시킬 수 있는 툴을 만들고 싶었습니다. 그 노력의 산물로, RGT팀은 마이크로 프론트엔드 장착형 프레임워크인 Lattice를 탄생시킵니다.

새로운 접근

요즘 웹 애플리케이션들이 다른 디펜던시[4]를 활용해서 UI 구성을 짜는 걸 흔히 볼 수 있는데요. 소스코드 파일을 변환하는 빌드 타임 내에 디펜던시를 번들링[5]하는 기존의 방식은 유연성이 부족한 탓에 사용자들이 자유롭게 활용하기에는 어려움이 많았습니다. 우리는 소스의 수에 구애받지 않으면서도, 다른 애플리케이션에서 그때그때 필요한 외부 디펜던시를 가져와 사용할 수 있는 툴을 만들고 싶었습니다.

그래서 다음과 같이 높은 수준의 목표를 설정하게 되었습니다.

중복 패키지 생성 최소화: 기존의 프론드엔드 코드를 재사용하도록 장려하고 사용자 인터페이스(UI) 기능을 축소시키는 새로운 패키지 생성을 가급적 피합니다. 여러 패키지가 한 기능을 공유하게 되면 애플리케이션 관리는 더욱 어려워질 가능성이 높기에, 우리는 기존에 잘 알려진 자바스크립트 라이브러리 중 하나인 리액트(React) 패러다임을 활용해서 애플리케이션이 핵심 기능을 확장할 수 있는 방법을 모색했습니다.

약한 의존성[6] : 호스트 애플리케이션은 https를 거쳐 넷플릭스에 자체적으로 호스팅 한 원격 번들로 모듈을 참조할 수 있습니다. 이런 번들은 웹팩 모듈 페더레이션 이나 네이티브 자바스크립트 모듈 처럼 이미 채택된 기준을 준수해서 만든 번들로, RGT팀이 아닌 다른 외부 팀도 사용할 수 있습니다.

배열은 완벽하게, 결합은 느슨하게 : 넷플릭스에 사용된 기준 프레임워크와 라이브러리에 완전히 들어맞도록 배열합니다. 플러그인은 불필요한 코드 템플릿 사용을 자제하여 핵심 기능을 실현하는데 초점을 두어야 하고, 자바의 코드인 API wrapper를 사용하는 번거로움 없이 실행할 수 있어야 합니다.

메타데이터 중심 : 애플리케이션 수명주기 상에서 언제든지 자유롭게 주입할 수 있는 배열 형태가 해당 플러그인 모듈을 정의합니다. 프레임워크는 등록과 등록취소가 자유로워야 하고, 플러그인 확장은 필요한 경우에만 적용합니다.

신속한 개발: 불필요한 빌드와 배치를 생략하여 개발 사이클을 줄입니다. 타입 스크립트 선언(TypeScript declarations)을 통해 모든 콘텍스트가 사전에 이용 가능한 플러그인을 개발할 수 있고, 애플리케이션이 정의한 엄격한 인터페이스에 맞춰 디자인하면 플러그인과 호스트를 동시에 개발할 수 있습니다.

이론적 예시

내장 Lattice 플러그인을 활용한 개발자 대시보드 애플리케이션

위의 예시를 한 번 살펴볼까요? 이 애플리케이션은 헤더와 콘텐츠 영역을 렌더링하고 컨트롤해서 사용자에게 명확한 기능성을 보여줍니다. 이 애플리케이션 출시 직후, 우리는 다른 툴에서 볼 수 있는 정보를 이 애플리케이션에도 볼 수 있으면 좋겠다는 피드백을 받았는데요. 새 프레임워크 Lattice에 피드백받은 내용을 반영해 다른 애플리케이션에 있는 기존 기능을 삽입할 수 있게 되었습니다.

Lattice 플러그인 호스트(뒤에서 더 자세히 다룰 예정입니다.)는 외부 플러그인인 워크플로(Workflows)와스피내커(Spinnaker ) 를 참조해서 기존 애플리케이션을 확장할 수 있게 합니다. 위 예시에서는 확장할 수 있는 두 영역, 즉 포탈 컴포넌트와 관련된 애플리케이션 콘텐츠와 설정 가능한 라우팅을 정의할 필요가 있습니다.

렌더링 처리를 위해 Lattice와 두 플러그인 워크플로와 스피내커가 함께 연속적으로 발생하는 이벤트를 아래와 같이 핸들링합니다.

Lattice 내의 디스패치 사이클

첫 번째로, Lattice가 두 플러그인을 비동기적으로 로딩합니다.

그다음으로 애플리케이션을 따라 이벤트가 흘러갈 수 있도록 프레임워크가 이벤트를 디스패치합니다. 마지막으로 위 예시에서처럼 워크플로가 자체적인 루트를 등록하고, 스피내커는 오버레이를 추가합니다.

리액트(React)로 실행하기

위 시나리오대로 흘러가려면, 호스트 애플리케이션은 Lattice 라이브러리를 가지고 있는 상태에서 외부 플러그인을 참조하는새 플러그인 호스트 PluginHost 를 추가해야 하는데요. 이 호스트는 애플리케이션과 로딩할 플러그인에 대한 상세한 정보를 필요로 합니다.

Lattice 플러그인 호스트로 리액트 애플리케이션 향상시키기

위의 예시처럼 우리는 useFetchPluginConfiguration 훅[7]을 사용해 외부 서비스로부터 메타데이터를 가져오는 테스트를 진행했습니다. 관리자는 이 훅을 사용해서 애플리케이션 소스 코드에 구애받지 않고 원하는 대로 마음껏 플러그인을 더하고 뺄 수 있습니다.

Lattice 프레임워크로 정의한 훅을 사용하면 플러그인이 해당 라우팅으로 접근할 수 있게 됩니다. usePluggableState 훅은 디폴트 애플리케이션 루트를 불러와 Lattice 프레임워크로 통과시킵니다. 그리고 이 AppRoutes 식별자[8]에 응답하는 플러그인은 다음과 같이 구체적인 루트를 삽입할 수 있게 됩니다.

Lattice 훅으로 기존 애플리케이션 상태 확장하기

아래에서 볼 수 있듯이 플러그인이 요소만 가지고 있으면 페이지에 어떤 리액트 요소든지 모두 삽입할 수 있습니다. 리액트 요소를 삽입하면 플러그인은이 AppContent 영역 내에서 렌더링 처리를 할 수 있게 됩니다.

Lattice Pluggable로 커스텀 칠드런(Custom Children) 렌더링

최종적인 애플리케이션 스니펫[9]을 아래 예시에서 확인할 수 있습니다.

Lattice 파헤치기

Lattice는 리액트 웹 애플리케이션이 활용하기 좋은 추상화 계층[10]을 제공하는 작은 프레임워크다.

Lattice를 활용해서 개발자들은 핵심 프로덕트에 집중할 수 있게 되었고, 외부 플러그인으로 커스터마이징이 가능한 애플리케이션 영역의 기능을 손쉽게 포함시킬 수 있게 되었습니다. 또 Lattice 훅으로 해당 요소를 확장해서 외부 상태를 사용할 수 있게 되었습니다.

Lattice 플러그인 모듈은 자바스크립트 기능으로, 원격 애플리케이션으로 실행이 가능한 모듈입니다. 이 모듈은 호스트 애플리케이션과 공유된 원격 요소(들) 사이를 끈끈하게 붙여주는 풀 역할을 합니다. 그리고 이 모듈은 애플리케이션 내에서 어떤 요소가 노출되어야 할지, 또 호스트가 제공하는 정보에 기반하여 이 요소를 어떻게 렌더링해야 할지 선언합니다.

Lattice Pluggable은 호스트 애플리케이션이 기준점이 되는 리액트 요소를 통해 마운트 포인트[11]를 노출시킵니다. 플러그인은 자체 콘텐츠로 이 리액트 요소를 처리할 수 있고, 처리를 중지할 수도 있습니다.

Lattice Custom 훅은 상태 리듀서 패턴(state reducer pattern)을 사용해서 상태를 처리합니다. 이 훅은 호스트 애플리케이션이 최초 상태를 유지할 수 있도록 필요시에 상태 수정을 가능하게 하며, 또 플러그인에 자체 데이터를 삽입할 수 있게 합니다.

Lattice 플러그인

호스트 애플리케이션 내의 Lattice 기능

Lattice의 핵심 기능은 바로 프레임워크 외부에서 정의된 웹팩 모듈 페더레이션, 네이티브 ES 모듈이나 프레임워크 외부에서 정의된 커스텀 실행을 통해 비동기적으로 원격 모듈을 로딩할 수 있다는 것인데요. 호스트 애플리케이션은 기본적인 애플리케이션 콘텍스트와 로딩하고자 하는 원격 모듈을 정의하는 배열을 Lattice에 제공합니다. 일단 이렇게 한 번 로딩되면, 이 플러그인에 대한 레퍼런스가 리액트 콘텍스트 사례에 내부적으로 저장됩니다.

Lattice로 연합된 모듈(Federated Module) 기능 노출하기

이제 플러그인 모듈은 완전히 새로운 기능뿐만 아니라 기존의 기능을 변경해서도 호스트 애플리케이션에 제공할 수 있습니다. 플러그인이 여러 애플리케이션에 걸쳐 보편적으로 사용 가능하려면 Lattice에서 사용 가능한 모든 애플리케이션이 기준 식별자를 실행할 수 있어야 하는데요. 대부분의 플러그인이 기존의 애플리케이션 기능을 확장하려 하겠지만 아직까지는 이런 기능성 확장이 보편적이지 않기 때문에 현재로써는 호스트 디자인 관련 지식이 절대적으로 필요합니다.

Lattice는 현재 무엇이 렌더링 처리되고 있는지 알고자 끊임없이 identifier 값(즉, 매직 스트링[12])을 요구합니다. Lattice 플러그인 호스트는 이전에 등록하고 로딩한 적이 있는 모든 플러그인으로 이 식별자를 디스패치합니다. 그러면 플러그인이 응답하고, 최종 반환 값이 컴포넌트 트리[13]에서 렌더링 됩니다. 이 모델을 통해, 플러그인은 해당 이벤트를 확장, 변경하거나, 경우에 따라서는 단순히 무시해 버릴 수도 있습니다. 이 프로세스는 리덕스(Redux)나 익스프레스 미들웨어 기능(Express Middleware functions) 접근법과 유사하다고 보시면 됩니다.

Lattice는 또 기존 애플리케이션 기능성을 확장하는데도 사용됩니다. 이렇게 하려면 플러그인이 반드시 호스트 애플리케이션 수명주기에서 사용되는 호스트 식별자와 데이터 모양을 잘 인지하고 있어야 합니다. 이게 불가능한 것처럼 들릴지 모르겠지만, 우리는 호스트 애플리케이션이 호스트와 플러그인 간에 공유되는 타입 스크립트 선언 프로젝트를 퍼블리싱할 수 있도록 합니다. 간단히 말해 우리가 Lattice를 통해 확장하는 모든 넷플릭스 내부 툴이 DefinitelyTyped 레포지토리를 가지고 있다고 생각하시면 이해하기 쉽습니다.

이렇게 우리는 개발자에게 완벽한 배열과 약한 결합으로 호스트 애플리케이션과 플러그인이 공유할 수 있는 최적의 개발 환경을 제공하고, 여기서 플러그인은 이미 선언한 인터페이스를 고수하며 개별적으로 분산된 형태에서 개발 가능해집니다.

무한한 가능성

애플리케이션 내부의 핵심 기능을 확장하는 데서 출발했지만, 어쩌다 보니 Lattice를 다른 방식으로도 활용할 수 있게 되었는데요. 단순히 if 명령을 쓰는 개념을 넘어, 한 걸음 물러서서 앞서 언급한 로직을 유도하는 도메인이 어떤 것인지 판단하고, 이 로직을 각각의 플러그인으로 어떻게 이동시킬지 생각해보는 단계까지 이르게 되었습니다.

또, 우리는 Lattice로 애플리케이션 안에서 좀 더 섬세한 영역을 쉽게 만들 수 있다는 사실을 알게 되었어요. 예를 들면, Lattice 식별자를 이용해서 개별적인 형태의 구성 요소를 렌더링 할 수 있고, 플러그인이 특정한 UI 요소에 기여하는 디자인을 할 수 있게 되었습니다. 이런 식으로 메타데이터 모델이 받쳐주는 포괄적인 툴을 만들 수 있게 되었고, 새로운 아웃 오브 박스 경험[14]도 구축할 수 있었습니다.

무엇보다도 가장 중요한 건, 서로 다른 플러그인을 실행시키기만 해도 상반되는 요구사항에 아주 쉽고 빠르게 반응할 수 있다는 것입니다.

향후 계획

Lattice는 이제 막 발걸음을 내딛기 시작하여 내부적으로 다른 팀의 반응을 살피고 있는 단계입니다. RGT팀 내부적으로 새로운 제품을 내부적으로 먼저 사용하며 발견한 문제점을 해결하고, 버그를 없애는 과정을 반복하며 Lattice로 마이크로 프론트엔드를 탄탄하게 완성할 수 있게 될 겁니다. Lattice의 성공 여부는 개발자의 경험에 크게 좌지우지됩니다. Lattice 이벤트의 수명주기를 잘 이해할 수 있는 개발자가 Lattice의 기능성을 충분히 입증할 수 있고 배열, 버저닝[15], 엔드 투 엔드 테스트[16]를 진행하며 우수한 퍼포먼스를 보여줄 수 있다면 Lattice는 성공할 가능성이 다분해 보입니다.

[1] 레버뉴/그로스 엔지니어링(Revenue and Growth engineering): 데이터와 소프트웨어 엔지니어링을 기반으로 한 다양한 실험을 통해 제품의 최적화된 사용자 경험(User Experience)을 만들고, 이를 통해 더 많은 사용자가제품을 지속적으로 사용하게 유도해서 수익(Revenue)과 매출 성장(Growth)을 도모하는 기술로, 최근 들어 많은 주목을 받고 있습니다.

[2] 마이크로 프론트엔드(Micro frontend): 전체 화면을 작동할 수 있는 작은 단위로 나누어 개발한 후 서로 조립하는 디자인 방식

[3] 프레임워크(Framework): 소프트웨어 어플리케이션이나 솔루션의 개발을 수월하게 하기 위해 소프트웨어의 구체적 기능들에 해당하는 부분의 설계와 구현을 재사용 가능하도록 협업화 된 형태로 제공하는 소프트웨어 환경

[4] 디펜던시(Dependencies) : 한 소프트웨어를 작동시키기 위해 필요한 라이브러리

[5] 번들링(Bundling) : 기능별 모듈을 한 데 묶어주는 과정

[6] 약한 의존성(Weak dependencies): 인터페이스를 사용해서 의존성을 낮추면 클래스 간 결합도가 약해져 관리와 확장이 편리해집니다.

[7] 훅(Hook) : 코드 내에서 프로그래머가 커스터마이징한 프로그래밍을 삽입할 수 있는 인터페이스

[8] 식별자(identifier): 데이터의 특성을 표시하기 위해 사용하는 기호

[9] 스니펫(Snippet): 재사용 가능한 소스 코드, 기계어, 텍스트의 작은 부분을 일컫는 프로그래밍 용어

[10] 추상화 계층(Abstraction layer): 특정한 집합의 기능의 자세한 부분을 숨기는 한 방법

[11] 마운트 포인트(Mount point): 외부 장치에게 대여해주기 위해 마련된 임의의 디렉토리 공간

[12] 매직 스트링(Magic string) : 외부적으로 드러나지 않고 숨어서 함수에 영향을 주는 입력 값

[13] 컴포넌트 트리(Component tree) : 컴포넌트 간에 데이터와 이벤트를 공유하는 구조

[14] 아웃 오브 박스 경험(Out of box experience): 엔드 유저가 제품을 구매하거나 배송 받고 난 이후의 최초로 박스를 개봉하는 순간의 경험

[15] 버저닝(Versioning) : 일정 기간 동안 수정되기 전의 파일을 보관하는 기능

[16] 엔드 투 엔드 테스트(End to end test): 애플리케이션이 온전히 동작하는 상태에서 사용자의 행동에 따라 애플리케이션의 각 부분이 정해진대로 동작하는지 확인하는 방식

(설계)Micro Frontend

마이크로 프론트엔드란?

특정 기준으로 프론트엔드를 나누는 개발방식으로 프론트앱에 여러 기능과 섹션이 존재하여 통합해서 관리하기 힘들때 사용

https://medium.com/bb-tutorials-and-thoughts/how-to-implement-micro-frontend-architecture-with-react-5ab172a0fec7

위 그림은 여러 앱으로 나누어서 백엔드 또는 서로간에 소통하는 방식을 나타낸다.

마이크로 서비스 vs 마이크로 프론트엔드

개발방식

Micro FrontEnds

배포방식

모놀리식 배포 마이크로 프론트엔드 식 배포

마이크로 프론트엔드 장점

앱을 이해하기 쉬워진다.

각 앱의 규모가 작아지기에 이해하기 쉬워진다.

각 앱이 독립적이 된다.

분리되서 개발하기에 서로 독립적이다.

더 쉽게 개발하고 배포할 수 있다.

각 앱마다 단일팀에서 개발 가능하기 때문에 개발과 배포가 쉽다.

개발 속도가 빨라진다.

각 앱이 별도의 팀으로 구성될 수있기에 개발 속도가 빨라진다.

CI/CD가 쉬워진다.

각 앱을 개별적으로 통합하고 배포할 수 있으므로 CI/CD 프로세스가 훨씬 쉬워진다.

독립 스택 및 버전 사용

각 앱마다 서로 같은 기술스택을 사용하더라도 부분적으로 최신버전을 도입하여 유연하게 테스트 할 수 있다.

코드를 공유하지 않는다.

서로간에 공유되는 코드가 많을 수록 버그 발생률이 올라가는데, 마이크로 프론트엔드에선 이러한 코드공유를 하지 않음

아키텍처의 확장이 유연해진다.

나눠져 있기 때문에 새로운 확장이 편해진다.

마이크로프론트엔드 단점

페이로드 크기

공통 컴포넌트를 공유하는게 아닌 각 앱이 독립적이므로 각 앱의 자바스크립트 번들은 서로 같은 내용을 중복적으로 포함하게 된다. 그럼 중복된 만큼 전체 페이로드의 크기도 커지게 된다.

복잡성과 일관성

여러 앱으로 분산되기 때문에 모든 앱들을 통합하거나 빌드 배포할때 복잡해지고 고려해야할 사항들이 많아진다. 또한 각 앱마다 각기 다른 팀들이 배정된다면 서로의 일관성을 유지하기가 어려워질 수 있다.

앱을 분할하는 방법

기능별

각 앱을 기능별로 나눠서 필요에 따라 DOM에서 마운트 / 언마운트 시킨다.

섹션별

각 앱을 해당 페이지의 구역별로 나눠서 개발을 진행한다.

페이지별

각 앱을 페이지 별로 나눠서 개발을 진행한다.

도메인별

각 앱을 도메인 기반으로 나눠서 개발을 진행한다. 가장 일반적으로 사용하는 분할방식이다.

구현방법

런타임 통합방식

Webpack Module Federation(웹팩 모듈 연합) 런타임 통합

웹팩이란? 여러 자바스크립트 파일들을 하나로 만들어서 압축시켜주므로 서버와의 접속횟수를 줄이고 성능을 최적화 시켜줌.

웹팩모듈연합은 이러한 웹팩의 최신버전인 웹팩5의 추가기능이다. react-create-app은 웹팩4를 지원하므로 커스텀설정해야한다.

이걸 사용하면 해당 앱이 다른앱의 코드를 동적으로 로드할 수 있다.

https://medium.com/bb-tutorials-and-thoughts/7-different-ways-to-implement-micro-frontends-with-react-907b5e262230

위그림의 로컬모듈은 최종 빌드할 모듈이 된다. 각 리모트모듈은 런타임에 동적으로 로딩시킨다.

iframe 런타임 통합

iframe은 html안에 다른 문서나 컨텐츠들을 넣을 수 있는 태그이다. 다른 도메인과 포트의 앱 문서도 넣을 수도 있다.

분기할 페이지의 도메인들을 객체로 정의해 놓고 해당 window의 location에 따라 iframe의 src를 바꿔주고 있다. iframe이 쉽고 간단하지만 iframe이 웹을 크롤링할때 문제를 끼칠수 있고 보안적 문제들이 존재하는 단점이 있다.

자바스크립트 런타임 통합

각 앱페이지를

iframe과 달리 꼭 해당 페이지를 보여주지 않고 단지 로드만 시킬수 있고 진입점 함수를 이용해 해당 페이지와 통신도 가능하다.

서버측 통합방식

nginx 서버 통합

nginx는 기존 Apach에 비해 경량화된 서버이다.

브라우저에서 라우팅한 경로를 Nginx서버에서 라우팅해준다. 즉 서버에서 분기에 따라 html파일을 결정해 통합 후 뿌려준다.

빌드타임 통합

각 앱들이 별도로 빌드 후 컨테이너앱에서 한꺼번에 통합하여 빌드하는 방식으로, 먼저 각 앱들을 패키지로 개시한다.

{ "name" : "@feed-me/container" , "version" : "1.0.0" , "description" : "음식 배달 웹 앱" , "dependencies" : { "@feed-me/browse-restaurants " : "^1.2.3" , "@feed-me/order-food" : "^4.5.6" , "@feed-me/user-profile" : "^7.8.9" } }

위의 코드는 앱의 의존성 정보를 기록해주는 설정파일이다. 이렇게 최종 컨테이너 앱을 빌드할때 나눠진 앱들의 패키지들을 의존성으로 추가함으로써 실행했을때 어떻게 작동할지 모두 알고 있으므로 런타임 통합에 비해 안정적인 통합 방식이다.

스타일링

css의 경우 마이크로 프론트엔드시에 여러 앱들 사이에 중복될 수 있다. 이러한 경우엔 BEM 같은 엄격한 이름규칙을 사용하거나 Sass를 사용해 네임스페이스 형식으로 해결하거나 Shadow DOM을 사용해 스타일을 격리 시킬 수 있다.

각 앱들사이에 어떻게 통신 시킬 수 있을까?

이벤트 사용

예시는 iframe으로 foo안에 bar를 넣었고 버튼을 누르면 bar로 메시지를 보내고자 한다. postMessage를 이용한다.

foo에서 보낸 이벤트는 bar로 들어오고, 미리 정의된 EventListener로 전달받는다.

그외엔 리액트처럼 데이터를 아래 컴포넌트에 전달하는 방식이나 라우팅을 통해 매개변수를 전달하는 방식 등이 있다.

백엔드와 통신은 어떻게 할까?

BFF(Backend for Frontend)패턴

각 앱들은 전용 백엔드 인터페이스를 갖는다. 예시로, 모바일 프론트앱은 거기에 필요한 모바일 백엔드 서비스들을 연동시킨 전용 모바일 BFF와 통신하고, 웹 전용 프론트앱은 거기에 필요한 웹 백엔드 서비스들을 연동시킨 전용 웹 BFF와 통신하는 식이다.

이렇게 함으로써 더효과적으로 각 프론트엔드 앱 환경에 맞는 서비스를 제공할 수 있다.

React와 single-spa를 사용한 마이크로 프론트엔드 프로젝트 만들기

single-spa란?

여러 자바스크립트앱들을 통합하기 위한 프레임워크이다. single-spa를 사용하면 React,Angular, 등 여러 프레임워크와 같이 사용할 수 있고, 런타임에 지연로드를 지원하여 초기 로딩시간을 개선할 수 있다.

적용방식

해당 프레임워크를 설치해 준다.

yarn global add create-single-spa

root-config디렉토리를 만들고 그 안에서 create-single-spa 해준다.

mkdir single-spa-demo-root-config cd single-spa-demo-root-config npx create-single-spa

그 후에 이렇게 옵션을 선택할 수 있는데 여기서 프로젝트의 디렉토리, 생성옵션, 패키지매니저, 타입스크립트 여부 등을 체크 하면

이렇게 Single-spa프로젝트가 생성된다.

douzonebf-root-config.ts : 는 모든 리모트앱들을 등록하는 곳이다.

index.ejs : 컴파일 후 index.html로 쓰여질 파일로써 다른 앱에서 쓰일 공통파일들을 가지고 있다.

yarn start 후 localhost:9000 접속시 이런 초기화면이 나온다.

이번엔 2개의 리모트앱을 만들어 보겠다.

아까와 똑같이 폴더를 만들고 npx create-single-spa해준다.

아까와 다른것은 생성타입이 root config가 아닌 single-spa application / parcel로 해준다.

해당 앱은 react를 사용할 것이므로 react를 선택하고 주의할점은 Organization name을 아까와 같이 douzonebf로 해준다.

root-config파일 내부에 공통파일을 놓는곳인 index.ejs안에 systemjs-importmap에 다음과 같이 react와 react-dom의 cdn 주소를 추가해준다.

그 후 root-config의 src안의 douzonebf-root-config.ts의 resisterApplication에 spa-1의 package.json파일에 써진 name 프로퍼티를 복사해 등록한다.

이 registerApplication 함수에 등록됨으로써 spa-1앱이 마이크로프론트로 관리되는 것이다.

후에 이렇게 index.ejs에 추가적으로 설정을 더해준다.

spa-2에서도 똑같이 진행을 해주고 각 앱의 root 컴포넌트를 이렇게 바꾸면

두 앱이 app-config에 통합된걸 확인할 수 있다.

Micro-frontend React, 점진적으로 도입하기

Day 2. Session

Micro-frontend React, 점진적으로 도입하기

거대한 모놀리식 Django 프로젝트에 현대적인 프론트엔드 인프라를 구축한 사례를 공유합니다. 어떻게 해야 오래된 코드 베이스를 대대적으로 수정하지 않으면서도, 최신 프론트엔드 기술들을 적극적으로 도입해나갈 수 있을지 고민한 경험을 공유합니다.

발표자료 다운로드 공유

강연에 사용된 자료 출처는 발표자료에서 참고해주세요.

리액트를 사용하여 마이크로프론트엔드 개발하는 방법: 단계적인 가이드

https://blog.bitsrc.io/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd

Rumesh Eranga Hapuarachchi, 2020, 7월 28일

마이크로서비스들의 향상으로, 커다란 애플리케이션들은 많은 면에서 혜택을 받았다. 효율적으로 개발하고, 배치하고, 애플리케이션 백엔드의 개별 조각들의 규모를 확장하는 것을 도와준다. 여전히, 많은 사람들은 프론트엔드에도 유사한 문제들이 존재한다는 것을 깨달았다. 그곳이 우리가 전형적으로 거대한 프론트엔드를 마이크로프론트엔드로 분해하기 시작하는 지점이다.

마이크로프론트엔드는 한 팀이 하나의 유닛으로 독립적으로 개발하고, 시험하고, 배포할 수 있는 프론트엔드의 조각이다. 그러나 우리는 이 조각들을 함께 붙여서 단일한 웹 애플리케이션으로 사용자에게 보여주는 것을 확실히 해야 한다.

마이크로프론트엔드들 조립을 위한 전략들

주로 두 개의 접근 방법이 있다. 하나는 각 마이크로프론트엔드를 구성하고 호스팅하는 단일한 컨테이너 앱을 사용하는 것이다. 또 다른 접근 방법은 각각 서로 다른 곳으로 찾아갈 수 있는 URL(통합 포인트)과 파라미터들을 알고 있어서 이것들을 따로따로 호스팅 하는 것이다. (웹사이트의 페이지들과 유사)

이 글에서는 컨테이너 앱 접근 방식을 사용했다. 마이크로엔드들을 컨테이너 앱으로 합치는 방법은 여러가지가 있다. 이러한 방법들은 컴포지션을 사용한 서버사이드에서부터 프론트엔드 빌드 타임에서 통합, 그리고 런타임에서 등 다양하다. 게다가 만약 당신이 런타입 통합을 택했다면 아이프레임을 사용한 애플리케이션 통합, 자바스크립트를 사용한 통합, 또는 웹 컴포넌트들을 사용한 통합 등 다수의 선택지를 가지게 된다.

이 글은 꽤 단순한 작업흐름을 증명해 보이는 것이지, 마이크로 프론트엔드 디자인을 실행할 때 예상되는 모든 도전들로 다 들어가진 않는다. 한 가지 분명한 도전은 저장소(각 마이크로 프론트엔드의 저장소) 간 컴포넌트들을 공유하는 것이다. 이것은 일관적인 UI와 유지보수 가능하고 확장성 있는 프로젝트를 위해 결정적이다.

이 도전은 보통 컴포넌트들을 공유하고 관리하는 도구이자 플랫폼인 빗-Bit(깃헙-Github)을 사용하여 해결한다. 빗은 모어떤 프로젝트에서 온 컴포넌트들이든 빠르고 쉽게 공유하게 해준다. 이것은 또 호스팅, 기록, 그리고 공유되는 컴포넌트들을 조직하는 플랫폼도 제공한다.

리액트를 사용하여 마이크로 프론트엔드 개발하기

현실 세계에서 마이크로 프론트엔드는 그 규모의 양이 꽤 된다. 하지만 이 예제에서는 무작위로 고양이들과 강아지들의 이미지를 보여주는 리액트로 만들어진 간단한 웹 애플리케이션을 살펴보자.

나는 위 앱의 코드를 마이크로프론트엔드로 분해하여 각 단계마다 관련 있는 코드 토막들을 자세히 보여주겠다. 그리고나서 우리는 컨테이너 앱을 기저를 이루는 호스트로 사용하여 그것들을 살펴볼 것이다.

1단계: 리액트를 사용하여 기저 프로젝트 구조 만들기

이 애플리케이션에서 고양이들과 강아지들 컴포넌트들을 분리하여 각각 마이크로 프론트엔드를 대표하게 한다. 이러한 접근은 이것들을 단일한 앱으로 붙이는 방법을 넓은 범위에서 고를 수 있게 더 많은 융통성을 준다.

이제 마이크로 프론트엔드의 개념에 익숙해졌으니 지금까지 배운 것들로 무언가를 실행해볼 시간이다. 우리는 여기서 같은 고양이들과 강아지들의 예시를 사용할 것이다. 리액트를 사용하여 컨테이너 앱, 고양이 앱, 강아지 앱을 만들 것이다.

맨 처음부터 시작해서 아래의 단계들을 밟으면 된다. 리액트 기초와 익숙하고 이러한 애플리케이션을 마이크로 프론트엔드들로 전환하는 작업을 하고 싶다면 이 링크를 사용하여 보일러플레이트를 다운 받아 마이크로 프론트엔드로 전환 섹션으로 건너뛰기 하면 된다.

1. create-react-app을 사용하여 세 개의 리액트 프로젝트들을 만든다. 나는 이것들을 container, cats, dogs로 이름 지을 것이다.

npx create-react-app container

npx create-react-app cats

npx create-react-app dogs

2. 이제 우리 앱에서 가장 작은 마이크로프론트엔드인 Dogs 앱에서 작업을 하자. 무작위로 강아지 사진을 보여주는 것이 이 프로젝트의 의도다. 사용자는 버튼을 클릭하여 새로운 강아지의 이미지를 얻을 수 있다.

먼저 App.css 파일의 모든 것을 지운다. 일단 여기서는 아무 스타일링도 필요하지 않다.

그리고나서 App.js의 내용을 다음과 같이 업데이트 한다.

import React, { useState, useEffect } from "react"; import logo from "./logo.svg"; import "./App.css"; function App() { const [dogImg, setDogImg] = useState(null); const fetchDoggo = () => { setDogImg(""); fetch(`https://dog.ceo/api/breeds/image/random`) .then((res) => res.json()) .then((dogInfo) => { setDogImg(dogInfo.message); }); }; useEffect(() => { if (dogImg === null) { fetchDoggo(); } }); return (

Doggo of the day

{dogImg !== "" ? (

doggo

) : (

Loading Image

)}

); } export default App;

이제 애플리케이션을 시작하여 시험해볼 수 있다. 다음과 같은 페이지를 볼 수 있을 것이다.

yarn start

3. 이제 고양이 애플리케이션의 기능을 업데이트할 시간이다. 이 애플리케이션에서 특별한 점은 이것이 라우팅을 지원한다는 것이다. 우리 애플리케이션에 두 개의 루트가 있다. 하나는 뿌리 URL로, 고양이를 무작위로 보여줄 것이다. 그 다음 것은 cats/{greeting}을 듣고 greeting 파라미터와 함께 고양이의 이미지를 보여주어야 한다.

먼저, react-router-dom과 history를 추가하여 라우팅과 히스토리 지원을 더한다. 이를 위해, 고양이 애플리케이션으로 가서 다음을 실행시킨다.

yarn add react-router-dom history

이제 여기서 아무런 스타일링이 필요하지 않으니 App.css의 모든 것을 지운다. 그 후 RandomCat과 GreetingCat 이라는 두 개의 컴포넌트들을 src 디렉토리 안에 만든다.

import React, { useState, useEffect } from "react"; export default function RandomCat() { const [randomCatImg, setRandomCatImg] = useState(null); const fetchRandomCat = () => { setRandomCatImg(""); fetch(`https://aws.random.cat/meow`) .then((res) => res.json()) .then((catInfo) => { setRandomCatImg(catInfo.file); }); }; useEffect(() => { if (randomCatImg === null) { fetchRandomCat(); } }); return (

Cat of the day

{randomCatImg !== "" ? (

Cat

) : (

Loading Image

)}

); }

다음 단계로 GreetingCat 컴포넌트를 만들어야 한다. 이 컴포넌트의 목적은 greeting 파라미터를 듣고 그에 따라 이미지를 생성하는 것이다.

import React from "react"; export default function GreetingCat(props) { const { greeting } = props.match.params; const greetingCatUrl = `https://cataas.com/cat/says/${greeting}`; return (

Greet me

{!greeting ? (

Needs a greeting

) : (

Cat

)}

); }

이제 고양이 앱에 라우팅을 더할 시간이다. 이를 위해 App.js를 수정하여 다음 내용을 추가한다.

import React from "react"; import { Router, Route } from "react-router-dom"; import { createBrowserHistory } from "history"; import GreetingCat from "./GreetingCat"; import RandomCat from "./RandomCat"; import "./App.css"; const defaultHistory = createBrowserHistory(); function App({ history = defaultHistory }) { return ( ); } export default App;

고양이 애플리케이션은 다음과 같이 보일 것이다.

고양이 앱의 메인 루트 고양이 앱의 Greeting Cat 루트

이제 두 개의 애플리케이션이 있다. 다음 단계는 컨테이너 애플리케이션을 만드는 것이다. 우리의 목적은 강아지 앱과 고양이 앱(RandomCat 컴포넌트)을 뿌리에 두고 여기로 가면 GreetingCat 컴포넌트로 보여주게 되는 것이다.

먼저, react-router-dom과 history 패키지들을 컨테이너 앱에 더한다.

yarn add react-router-dom history

그 후 App.js 를 다음 내용으로 업데이트한다.

import React, { useState } from "react"; import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom"; import { createBrowserHistory } from "history"; import "./App.css"; const defaultHistory = createBrowserHistory(); function Header() { return (

😻 Cats and Dogs 🐕

Random pics of cats and dogs

); } function Home({ history }) { const [input, setInput] = useState(""); const handleOnClick = () => { history.push(`/cat/${input}`); }; return (

setInput(e.target.value)} />

); } function App({ history = defaultHistory }) { return ( } /> ); } export default App;

컨테이너 애플리케이션에 스타일링을 더 할 시간이다. App.css의 내용을 다음 스타일링들로 교체한다.

.banner { background: #f5f5da; padding: 30px; text-align: center; border-radius: 20px; } .banner-title { color: #2d3d29; } .home { margin-top: 30px; width: 100%; text-align: center; } .content { margin: auto; width: 70%; padding: 10px 0px; background: #f5f5da; display: inline-block; border-radius: 10px; } .cat { display: inline-block; width: 45%; } .dog { display: inline-block; width: 45%; }

2단계: 마이크로 프론트엔드로 전환하기

우리가 해결해야 할 첫번째 문제는 컨테이너 앱이 어떻게 마이크로 프론트엔드를 알게하는 것이냐다. 이것을 제기하기 위해 우리는 .env 파일을 이용하여 마이크로 프론트엔드 목록을 유지보수 할 수 있다.

로컬 개발에서 모든 마이크로 프론트엔드들은 로컬호스트에서 작동할 것이니 우리는 MFE들에 여러 포트들을 배분할 필요가 있다. 다음 포트들을 사용하자.

컨테이너 앱: 포트 3000 강아지 앱: 포트 3001 고양이 앱: 포트 3002

컨테이너 앱의 뿌리 레벨에 다음 내용으로 .env 파일을 생성한다.

REACT_APP_DOGS_HOST=http://localhost:3001 REACT_APP_CATS_HOST=http://localhost:3002

이제 우리는 어디로부터 각 마이크로 프론트엔드를 가져올 수 있는지 안다. 그런데 컨테이너 앱은 마이크로 프론트엔드들을 앱의 연관 섹션에 넣는 방법을 어떻게 알 수 있는가?

이 질문을 언급하기 위해, 우리의 모든 마이크로 앱 개발 팀들은 입구를 어떻게 정의할 것인지와 앱들을 어떻게 발견할 것인지에 대해 동의해야 한다. 이 예시에서 우리는 이를 해결하는 방법은 빌드 스크립트로 생성된 asset-manifest.json을 사용하는 것이다.

{ "files": { "main.css": "/static/css/main.5f361e03.chunk.css", "main.js": "/static/js/main.81def56f.chunk.js", "main.js.map": "/static/js/main.81def56f.chunk.js.map", "runtime-main.js": "/static/js/runtime-main.f9edd30b.js", "runtime-main.js.map": "/static/js/runtime-main.f9edd30b.js.map", "static/js/2.fd646533.chunk.js": "/static/js/2.fd646533.chunk.js", "static/js/2.fd646533.chunk.js.map": "/static/js/2.fd646533.chunk.js.map", "index.html": "/index.html", "precache-manifest.53c6aa224b68ac094481ecedc920549c.js": "/precache-manifest.53c6aa224b68ac094481ecedc920549c.js", "service-worker.js": "/service-worker.js", "static/css/main.5f361e03.chunk.css.map": "/static/css/main.5f361e03.chunk.css.map", "static/js/2.fd646533.chunk.js.LICENSE.txt": "/static/js/2.fd646533.chunk.js.LICENSE.txt", "static/media/logo.svg": "/static/media/logo.5d5d9eef.svg" }, "entrypoints": [ "static/js/runtime-main.f9edd30b.js", "static/js/2.fd646533.chunk.js", "static/css/main.5f361e03.chunk.css", "static/js/main.81def56f.chunk.js" ] }

자세히 들여다 보면 files 객체 안에 main.js 파일 경로가 있음을 볼 수 있다. 빌드 스크립트가 전체 애플리케이션을 main.js로 묶을 것이다. 이 main.js 안에 컴포넌트를 구현하고 탈착할 함수를 가지고 있어야 한다. 다음 방식을 사용하자.

구현 함수 이름: render{ 앱 이름 }

탈착 함수 이름: unmount{ 앱 이름 }

예시: 강아지 앱 함수 이름들

renderDogs

unmountDogs

이제 우리는 컴포넌트를 어떻게 구현하고 탈착하는지 아니까, 컨테이너 앱 안에 이러한 마이크로 프론트엔드들을 보유하고 있을 포괄적인 컴포넌트를 생성할 시간이 되었다. 컨테이너 앱에 MicroFrontend 컴포넌트를 추가함으로써 시작하자. 여기서 나는 캠 잭슨의 마이크로 프론트엔드 컴포넌트를 기초로 사용하였다.

컨테이너 앱 안에 src/MicroFrontend.js 를 생성하기

import React, { useEffect } from "react"; function MicroFrontend({ name, host, history }) { useEffect(() => { const scriptId = `micro-frontend-script-${name}`; const renderMicroFrontend = () => { window[`render${name}`](`${name}-container`, history); }; if (document.getElementById(scriptId)) { renderMicroFrontend(); return; } fetch(`${host}/asset-manifest.json`) .then((res) => res.json()) .then((manifest) => { const script = document.createElement("script"); script.id = scriptId; script.crossOrigin = ""; script.src = `${host}${manifest.files["main.js"]}`; script.onload = () => { renderMicroFrontend(); }; document.head.appendChild(script); }); return () => { window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`); }; }); return

; } MicroFrontend.defaultProps = { document, window, }; export default MicroFrontend;

이 마이크로 프론트엔드 컴포넌트는 name, host, history를 파라미터로 받을 것이다. 17행을 보면 이 컴포넌트가 asset-manifest.json 파일을 호스트로부터 먼저 가져와서 main.js 파일을 사용하여 스크립트 객체를 만드는 것을 볼 수 있을 것이다. 그 후 이것은 render 함수를 이용하여 컴포넌트를 장착시킨다.

다음 단계는 이 컴포넌트를 사용하여 고양이 애플리케이션과 강아지 애플리케이션을 우리의 앱에 장착시킨다. 이를 위해 컨테이너의 App.js 파일을 다음과 같이 수정한다.

import React, { useState } from "react"; import { BrowserRouter, Switch, Route } from "react-router-dom"; import { createBrowserHistory } from "history"; import MicroFrontend from "./MicroFrontend"; import "./App.css"; const defaultHistory = createBrowserHistory(); const { REACT_APP_DOGS_HOST: dogsHost, REACT_APP_CATS_HOST: catsHost, } = process.env; function Header() { return (

😻 Cats and Dogs 🐕

Random pics of cats and dogs

); } function Dogs({ history }) { return ; } function Cats({ history }) { return ; } function GreetingCat({ history }) { return (

); } function Home({ history }) { const [input, setInput] = useState(""); const handleOnClick = () => { history.push(`/cat/${input}`); }; return (

setInput(e.target.value)} />

); } function App() { return ( ); } export default App;

10행에서 13행까지는 .env 파일로부터 앱 서버들을 읽기 위한 목적이다. 24행에서 41행까지를 읽는다면 우리가 마이크로 프론트엔드들을 사용하여 세 가지 컴포넌트들을 만들었음을 알아챌 것이다.

81~82행은 애플리케이션에다 앱이 이 루트들을 구현해야 한다는 것을 알린다. 뿌리 경로에서 우리는 고양이 앱과 강아지 앱 둘 다를 구현한다. 그러나 /cat/:greeting 경로에서 우리는 오직 고양이 앱만 구현할 것이다. 같은 루트가 고양이 마이크로엔드를 위해서도 보일 것이다. 그래서 기대한대로 그것이 고양이의 이미지를 글자와 함께 구현할 것이다.

컨테이너는 이것이 다다. 이제 기존의 마이크로 프론트엔드드를 수정할 시간이다.

3단계: 고양이와 강아지 마이크로 프론트엔드 앱들을 수정하기

고양이 마이크로프론트엔드 앱을 수정하기

react-app-rewired, history, 그리고 react-router-dom을 반드시 고양이 애플리케이션에 더해야 한다.

yarn add react-app-rewired history react-router-dom

컨테이너 앱이 MFE를 로딩하려할 때 모든 것이 하나의 자바스크립트 파일에 있어야 한다. 그런데 위의 asset-manifest.json 파일을 보면 거기에 다수의 덩어리가 이용 가능하다는 것을 볼 수 있을 것이다. 우리는 이러한 덩어리들을 비활성화 시켜야 한다. 이를 위해 우리는 react-app-rewired 패키지를 사용할 것이다. 이것을 사용하여 우리는 앱을 꺼내지 않고 빌드 환경설정을 덮어쓰기 할 수 있다. 고양이 애플리케이션의 뿌리 레벨에 config-overrides.js를 생성하고 다음 내용을 넣는다.

module.exports = { webpack: (config, env) => { config.optimization.runtimeChunk = false; config.optimization.splitChunks = { cacheGroups: { default: false, }, }; config.output.filename = "static/js/[name].js"; config.plugins[5].options.filename = "static/css/[name].css"; config.plugins[5].options.moduleFilename = () => "static/css/main.css"; return config; }, };

그 다음 단계는 package.json 파일에 이 덮어쓰기를 사용하라고 지시하는 것이다. script 섹션을 다음과 같이 업데이트 한다. 그러면 이것이 모든 덩어리들을 제거하고 모든 것을 main.js로 묶었음을 알 수 있다.

"scripts": { "start": "PORT=3002 react-app-rewired start", "build": "react-app-rewired build", "test": "react-scripts test", "eject": "react-scripts eject" }

단위화를 무시하기 위해 환경설정 한 asset-manifest.json

우리의 모든 마이크로 프론트엔드 컨테이너들이 서로 다른 하위 도메인들에 호스팅되므로 우리는 마이크로 프론트엔드들 안에 CORS를 만드시 활성화시켜야 한다. 이를 위해 src/setupProxy.js를 생성하고 다음 내용을 더한다.

module.exports = (app) => { app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); next(); }); };

고양이 마이크로 프론트엔드를 위해 필요한 마지막 수정은 그것의 index.js를 render 함수와 unmount 함수와 함께 업데이트 하는 것이다. 이것을 위해 index.js를 다음 내용으로 변경한다.

import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; window.renderCats = (containerId, history) => { ReactDOM.render( , document.getElementById(containerId), ); serviceWorker.unregister(); }; window.unmountCats = containerId => { ReactDOM.unmountComponentAtNode(document.getElementById(containerId)); }; if (!document.getElementById('Cats-container')) { ReactDOM.render(, document.getElementById('root')); serviceWorker.unregister(); }

강아지 마이크로 프론트엔드 앱 수정하기

고양이 애플리케이션과 동일한 변화들을 반복해야 한다. 반드시 package.json 안에 있는 포트 주소를 3001로 바꾸고 index.js 안에 있는 함수 이름들을 변경한다.

모두 성공적으로 설정하였다면 아래의 최종 결과를 볼 수 있게 될 것이다.

복습

이 글에서 우리는 마이크로 프론트엔드들의 기초 및 리액트를 사용하여 마이크로 프론트엔드들을 실행하는 방법을 논의하였다.

먼저 우리는 어떻게 마이크로 프론트엔드를 컨테이너에 구현하고 탈착하는지 알고 있는 컨테이너 앱과 마이크로 프론트엔드라고 불리는 자리맡기 용 컴포넌트를 만들었다. 다음, 우리는 기존의 애플리케이션을 마이크로 프론트엔드로 전환하는 방법을 논의했다. 마지막으로 우리는 어떻게 주소창이 다른 마이크로엔드들과 소통하도록 사용하는지를 논의했다.

소스들은 다음 깃헙 저장소에서 찾을 수 있다.

마이크로프론트엔드를 위한 6가지 패턴 개요

조금 더 강력한 것은 다음을 사용하는 것과 같습니다. 바니시 리버스 프록시 .

또한 이것이 역사적인 SSI(Server-Side Include)의 (훨씬 더 유연한) 후속 제품인 ESI(약칭 Edge-Side Include)의 완벽한 사용 사례이기도 합니다.

FROM nginx:stable COPY ./dist/ /var/www COPY ./nginx.conf /etc/nginx/conf.d/default.conf CMD ['nginx -g 'daemon off;'']

Project Mosaic의 일부인 Tailor 백엔드 서비스에서도 유사한 설정을 볼 수 있습니다.

이 공간에서 다음 솔루션을 찾습니다.

이 접근 방식의 장단점은 무엇입니까?

을위한 : 완전히 격리됨

: 완전히 격리됨 을위한 : 사용자에게 임베딩된 모양

: 사용자에게 임베딩된 모양 을위한 : 매우 유연한 접근

: 매우 유연한 접근 와 함께 : 구성요소 간의 결합을 강제합니다.

: 구성요소 간의 결합을 강제합니다. 와 함께 : 인프라 복잡성

: 인프라 복잡성 와 함께 : 일관성 없는 사용자 경험

3. 클라이언트 측 구성

이 시점에서 우리는 역 프록시가 필요한가? 이것은 백엔드 구성 요소이므로 이것을 완전히 피하고 싶을 수 있습니다. 솔루션은 클라이언트 측 구성입니다. 가장 간단한 형태로 이것은 |_+_|를 사용하여 구현할 수 있습니다. 집단. 서로 다른 부분 간의 통신은 |_+_|를 통해 이루어집니다. 방법.

메모 : 자바스크립트 부분은 |_+_|의 경우 브라우저로 대체될 수 있습니다. 이 경우 잠재적인 상호작용성은 확실히 다릅니다.

이미 이름에서 알 수 있듯이 이 패턴은 역방향 프록시와 함께 발생하는 인프라 오버헤드를 방지하려고 합니다. 대신, 마이크로프론트엔드에는 이미 프론트엔드라는 용어가 포함되어 있기 때문에 전체 렌더링은 클라이언트에게 맡겨집니다. 이점은 이 패턴으로 시작하는 것이 서버리스가 가능할 수 있다는 것입니다. 결국 전체 UI를 예를 들어 GitHub 페이지 저장소에 업로드할 수 있으며 모든 것이 제대로 작동합니다.

설명된 대로 구성은 매우 간단한 방법(예: |_+_|)으로 수행할 수 있습니다. 그러나 주요 문제점 중 하나는 이러한 통합이 최종 사용자에게 어떻게 보일 것인지입니다. 리소스 요구 측면에서 중복도 상당히 상당합니다. 다른 부분이 독립적으로 운영되는 웹 서버에 배치되는 패턴 1과의 혼합은 확실히 가능합니다.

그럼에도 불구하고 이 패턴에서는 지식이 다시 필요합니다. 따라서 구성요소 1은 구성요소 2가 존재하고 사용해야 한다는 것을 이미 알고 있습니다. 잠재적으로 알아야 할 필요가 있습니다. 어떻게 그것을 사용합니다.

다음 부모를 고려합니다(즉, 전달된 애플리케이션 또는 웹사이트):

https://gist.github.com/dddbf80773ed8df03fcf20679f30c835

직접 통신 경로를 활성화하는 페이지를 작성할 수 있습니다.

http { server { listen 80; server_name www.example.com; location /api/ { proxy_pass http://api-svc:8000/api; } location /web/admin { proxy_pass http://admin-svc:8080/web/admin; } location /web/notifications { proxy_pass http://public-svc:8080/web/notifications; } location / { proxy_pass /; } } }

프레임 옵션을 고려하지 않으면 웹 구성 요소도 사용할 수 있습니다. 여기에서 사용자 정의 이벤트를 사용하여 DOM을 통해 통신을 수행할 수 있습니다. 그러나 이미 이 시점에서 클라이언트 측 구성 대신 클라이언트 측 렌더링을 고려하는 것이 합리적일 수 있습니다. 렌더링은 JavaScript 클라이언트(웹 구성 요소 접근 방식과 일치)가 필요함을 의미합니다.

이 공간에서 다음 솔루션을 찾습니다.

이 접근 방식의 장단점은 무엇입니까?

을위한 : 완전히 격리됨

: 완전히 격리됨 을위한 : 사용자에게 임베딩된 모양

: 사용자에게 임베딩된 모양 을위한 : 서버리스 가능

: 서버리스 가능 와 함께 : 구성요소 간의 결합을 강제합니다.

: 구성요소 간의 결합을 강제합니다. 와 함께 : 일관성 없는 사용자 경험

: 일관성 없는 사용자 경험 와 함께 : JavaScript가 필요할 수 있음 / 원활한 통합 없음

4. 클라이언트 측 렌더링

클라이언트 측 구성은 JavaScript 없이 작동할 수 있지만(예: 상위 또는 서로 간의 통신에 의존하지 않는 프레임만 사용) 클라이언트 측 렌더링은 JavaScript가 없으면 실패합니다. 이 공간에서 우리는 이미 작성 애플리케이션에서 프레임워크를 만들기 시작했습니다. 이 프레임워크는 가져온 모든 마이크로프론트엔드에서 존중되어야 합니다. 최소한 제대로 마운트하려면 이를 사용해야 합니다.

패턴은 다음과 같습니다.

클라이언트 측 구성에 아주 가깝죠? 이 경우 JavaScript 부분은 대체되지 않을 수 있습니다. 중요한 차이점은 서버 측 렌더링이 일반적으로 테이블에서 벗어난다는 것입니다. 대신 데이터 조각이 교환되어 뷰로 변환됩니다.

설계되거나 사용된 프레임워크에 따라 데이터 조각은 렌더링된 조각의 위치, 시점 및 상호 작용을 결정할 수 있습니다. 높은 수준의 상호 작용을 달성하는 것은 이 패턴에서 문제가 되지 않습니다.

이 공간에서 다음 솔루션을 찾습니다.

이 접근 방식의 장단점은 무엇입니까?

을위한 : 관심사의 분리를 강제합니다.

: 관심사의 분리를 강제합니다. 을위한 : 구성 요소의 느슨한 결합 제공

: 구성 요소의 느슨한 결합 제공 을위한 : 사용자에게 내장된 모양

: 사용자에게 내장된 모양 와 함께 : 클라이언트에서 더 많은 논리가 필요합니다.

: 클라이언트에서 더 많은 논리가 필요합니다. 와 함께 : 일관성 없는 사용자 경험

: 일관성 없는 사용자 경험 와 함께 : 자바스크립트가 필요합니다

5. 스파 구성

단일 기술을 사용하는 클라이언트 측 렌더링을 중단해야 하는 이유는 무엇입니까? JavaScript 파일을 가져와서 다른 모든 JavaScript 파일과 함께 실행하지 않는 이유는 무엇입니까? 이것의 이점은 여러 기술을 나란히 사용할 수 있다는 것입니다.

여러 기술을 실행하는 것(백엔드 또는 프론트엔드에서 독립적인 경우 — 허용, 백엔드에서 더 수용 가능함)을 실행하는 것이 좋은 것인지 피해야 하는 것인지는 논쟁의 여지가 있습니다. 그러나 여러 기술이 필요로 하는 시나리오가 있습니다. 협력.

내 머리 꼭대기에서 :

마이그레이션 시나리오

특정 타사 기술 지원

정치적 이슈

팀 제약

어느 쪽이든, 출현한 패턴은 아래와 같이 그릴 수 있습니다.

여기에서 무슨 일이 일어나고 있습니까? 이 경우 앱 셸과 함께 일부 JavaScript만 제공하는 것은 더 이상 선택 사항이 아닙니다. 대신 마이크로프론트엔드를 오케스트레이션할 수 있는 프레임워크를 제공해야 합니다.

다양한 모듈의 오케스트레이션은 마운팅, 실행, 마운트 해제와 같은 라이프사이클 관리로 귀결됩니다. 독립적으로 실행되는 서버에서 다른 모듈을 가져올 수 있지만 해당 위치는 응용 프로그램 셸에서 이미 알고 있어야 합니다.

이러한 프레임워크를 구현하려면 다음을 포함하는 스크립트 맵과 같은 최소한 일부 구성이 필요합니다.

Demo

수명 주기 관리는 위의 스크립트보다 더 복잡할 수 있습니다. 따라서 이러한 구성을 위한 모듈은 일부 구조가 적용된 상태로 제공되어야 합니다. 적어도 내보낸 |_+_| 그리고 |_+_| 기능.

이 공간에서 다음 솔루션을 찾습니다.

이 접근 방식의 장단점은 무엇입니까?

을위한 : 관심사의 분리를 강제합니다.

: 관심사의 분리를 강제합니다. 을위한 : 개발자에게 많은 자유를 줍니다.

: 개발자에게 많은 자유를 줍니다. 을위한 : 사용자에게 내장된 모양

: 사용자에게 내장된 모양 와 함께 : 중복 및 오버헤드 적용

: 중복 및 오버헤드 적용 와 함께 : 일관성 없는 사용자 경험

: 일관성 없는 사용자 경험 와 함께 : 자바스크립트가 필요합니다

6. 사이트리스 UI

이 주제는 자체 기사가 필요하지만 모든 패턴을 나열하고 있으므로 여기에서 생략하고 싶지 않습니다. SPA 구성의 접근 방식을 취하면 공유 런타임뿐만 아니라 서비스에서 스크립트 소스의 분리(또는 독립적인 중앙 집중화)만 놓치게 됩니다.

두 가지 모두 다음과 같은 이유로 수행됩니다.

분리는 UI와 서비스 책임이 혼동되지 않도록 합니다. 이것은 또한 서버리스 컴퓨팅을 가능하게 합니다.

공유 런타임은 이전 패턴에서 제공한 리소스 집약적인 구성에 대한 치료법입니다.

두 가지를 결합하면 서버리스 기능이 백엔드에 한 것처럼 프론트엔드에 이점이 있습니다. 또한 다음과 같은 유사한 문제가 있습니다.

런타임은 그냥 업데이트할 수 없습니다. 모듈과 일관성을 유지해야 합니다.

모듈을 로컬에서 디버깅하거나 실행하려면 런타임 에뮬레이터가 필요합니다.

모든 기술이 동일하게 지원되는 것은 아닙니다.

사이트리스 UI의 다이어그램은 다음과 같습니다.

이 디자인의 주요 장점은 유용하거나 공통적인 리소스의 공유가 지원된다는 것입니다. 패턴 라이브러리를 공유하는 것은 매우 의미가 있습니다.

전체적으로 아키텍처 다이어그램은 앞에서 언급한 SPA 구성과 매우 유사합니다. 그러나 피드 서비스와 런타임 연결은 추가적인 이점을 제공합니다(해당 영역의 모든 프레임워크에서 해결해야 하는 과제).

가장 큰 장점은 이러한 문제가 해결되면 개발 경험이 탁월해야 한다는 것입니다. 모듈을 유연한 옵트인 기능으로 취급하여 사용자 경험을 완전히 사용자 정의할 수 있습니다. 따라서 기능(각각의 구현)과 권한(기능에 액세스할 수 있는 권한) 사이의 명확한 분리가 가능합니다.

이 패턴의 가장 쉬운 구현 중 하나는 다음과 같습니다.

이것은 전역 변수를 사용하여 앱 셸에서 API를 공유합니다. 그러나 우리는 이미 이 접근 방식을 사용하는 몇 가지 문제를 확인했습니다.

하나의 모듈이 충돌하면 어떻게 됩니까?

종속성을 공유하는 방법(간단한 구현에서와 같이 각 모듈과 번들로 묶이는 것을 피하기 위해)?

올바른 타이핑을 하는 방법?

이것은 어떻게 디버깅되고 있습니까?

적절한 라우팅은 어떻게 이루어지나요?

이러한 모든 기능을 구현하는 것은 그 자체로 주제입니다. 디버깅과 관련하여 우리는 모든 서버리스 프레임워크(예: AWS Lambda, Azure Functions)와 동일한 접근 방식을 따라야 합니다. 나중에 실제처럼 작동하는 에뮬레이터를 출시해야 합니다. 로컬에서 실행 중이고 오프라인에서 작동한다는 점을 제외하고.

이 공간에서 다음 솔루션을 찾습니다.

이 접근 방식의 장단점은 무엇입니까?

을위한 : 관심사의 분리를 강제합니다.

: 관심사의 분리를 강제합니다. 을위한 : 오버헤드를 피하기 위한 리소스 공유 지원

: 오버헤드를 피하기 위한 리소스 공유 지원 을위한 : 일관되고 내장된 사용자 경험

: 일관되고 내장된 사용자 경험 와 함께 : 공유 자원에 대한 엄격한 종속성 관리 필요

: 공유 자원에 대한 엄격한 종속성 관리 필요 와 함께 : (잠재적으로 관리되는) 인프라의 다른 부분이 필요합니다.

: (잠재적으로 관리되는) 인프라의 다른 부분이 필요합니다. 와 함께 : 자바스크립트가 필요합니다

마이크로프론트엔드를 위한 프레임워크

마지막으로 제공된 프레임워크 중 하나를 사용하여 마이크로프론트엔드를 구현하는 방법을 살펴봐야 합니다. 우리는 간다 파이럴 이것이 내가 가장 잘 알고 있는 것이기 때문이다.

다음에서 우리는 두 가지 측면에서 문제에 접근합니다. 먼저 이 맥락에서 모듈(즉, 마이크로프론트엔드)부터 시작합니다. 그런 다음 앱 셸을 만드는 방법을 살펴보겠습니다.

우리가 사용하는 모듈의 경우 내 Mario5 장난감 프로젝트 . 이것은 Mario5라는 Super Mario의 JavaScript 구현으로 몇 년 전에 시작된 프로젝트입니다. 그 뒤를 이어 Mario5TS라는 TypeScript 튜토리얼/재작성이 이어졌으며, 그 이후로 최신 상태로 유지되었습니다.

우리가 사용하는 앱 셸의 경우 샘플 Piral 인스턴스 . 이것은 하나의 스윕에서 모든 개념을 보여줍니다. 또한 항상 최신 상태로 유지됩니다.

Piral 프레임워크에서 티켓 . 파일렛의 핵심에는 일반적으로 다음 위치에 있는 JavaScript 루트 모듈이 있습니다. src/index.tsx .

필렛

빈 파일렛으로 시작하면 다음 루트 모듈이 제공됩니다.

postMessage

|_+_|라는 특수하게 명명된 함수를 내보내야 합니다. 이것은 나중에 애플리케이션의 특정 부분을 통합하는 데 사용됩니다.

예를 들어 React를 사용하여 메뉴 항목이나 타일이 항상 표시되도록 등록할 수 있습니다.

타일에는 약간의 스타일이 필요하므로 파일렛에 스타일시트도 추가합니다. 좋아요, 지금까지는 너무 좋습니다. 직접 포함된 모든 리소스는 언제나 앱 셸에서 사용할 수 있습니다.

이제 게임 자체도 통합할 때입니다. 모달 대화 상자도 멋질 수 있지만 전용 페이지에 넣기로 결정했습니다. 모든 코드는 마리오.ts 표준 DOM에 대해 작동합니다. 아직 React가 없습니다.

React는 호스팅된 노드의 조작도 지원하므로 참조 후크를 사용하여 게임을 연결합니다.

이론적으로 게임을 다시 시작하거나 게임이 포함된 사이드 번들을 지연 로드하는 것과 같은 기능을 추가할 수도 있습니다. 지금은 소리만 |__+_| 호출을 통해 지연 로드됩니다. 기능.

파일렛 시작은 다음을 통해 수행됩니다.

https://gist.github.com/0678ac99f4653843159cf8e54ad06422

이것은 후드 아래에서 Piral CLI를 사용합니다. Piral CLI는 항상 로컬에 설치되지만 |_+_|와 같은 명령을 얻기 위해 전역적으로 설치할 수도 있습니다. 명령줄에서 직접 사용할 수 있습니다.

파일렛 구축은 로컬 설치로도 수행할 수 있습니다.

Microfrontend Send message to parent const results = document.querySelector('#results'); const messageButton = document.querySelector('#message_button'); function sendMessage(msg) { window.parent.postMessage(msg, '*'); } window.addEventListener('message', function(e) { results.innerHTML = e.data; }); messageButton.addEventListener('click', function(e) { sendMessage(Math.random().toString()); });

앱 셸

이제 앱 셸을 만들 차례입니다. 일반적으로 우리는 이미 앱 셸을 가지고 있지만(예: 샘플 앱 셸에 대해 이전 파일렛이 이미 생성됨) 모듈 개발이 어떻게 진행되는지 보는 것이 더 중요하다고 생각합니다.

Piral로 앱 셸을 만드는 것은 |__+_|를 설치하는 것만큼 간단합니다. 더 간단하게 만들기 위해 Piral CLI는 새 앱 셸의 스캐폴딩도 지원합니다.

어느 쪽이든 우리는 아마도 다음과 같이 끝날 것입니다.

const scripts = [ 'https://example.com/script1.js', 'https://example.com/script2.js', ]; const registrations = {}; function activityCheck(name) { const current = location.hash; const registration = registrations[name]; if (registration) { if (registration.activity(current) !== registration.active) { if (registration.active) { registration.lifecycle.unmount(); } else { registration.lifecycle.mount(); } registration.active = !registration.active; } } } window.addEventListener('hashchange', function () { Object.keys(registrations).forEach(activityCheck); }); window.registerApp = function(name, activity, lifecycle) { registrations[name] = { activity, lifecycle, active: false, }; activityCheck(name); } scripts.forEach(src => { const script = document.createElement('script'); script.src = src; document.body.appendChild(script); });

여기에서 세 가지 작업을 수행합니다.

모든 가져오기 및 라이브러리를 설정합니다. Piral 인스턴스를 생성합니다. 모든 기능 옵션 제공(가장 중요한 것은 필렛이 어디에서 왔는지 선언) 구성 요소와 우리가 정의한 사용자 정의 레이아웃을 사용하여 앱 셸을 렌더링합니다.

실제 렌더링은 React에서 수행됩니다.

앱 셸을 빌드하는 것은 간단합니다. 결국 전체 애플리케이션을 처리하기 위한 표준 번들러(Parcel)입니다. 출력은 웹 서버 또는 정적 저장소에 배치할 모든 파일이 포함된 폴더입니다.

가볼만한 곳

Siteless UI라는 용어를 만드는 데는 약간의 설명이 필요할 수 있습니다. 먼저 이름부터 시작하겠습니다. 보시는 바와 같이 서버리스 컴퓨팅에 대한 직접적인 참조입니다. 서버리스는 사용된 기술에 대한 좋은 용어일 수도 있지만 오해의 소지가 있고 잘못된 표현일 수도 있습니다. UI는 일반적으로 서버리스 인프라(예: Amazon S3, Azure Blob Storage, Dropbox)에 배포할 수 있습니다. 이것은 서버 측 렌더링을 수행하는 대신 클라이언트에서 UI를 렌더링할 때의 이점 중 하나입니다.

하지만 호스트 없이는 살 수 없는 UI를 가진 접근 방식을 따르고 싶었습니다. 런타임이 어딘가에 있어야 하고 그렇지 않으면 시작할 수 없는 서버리스 기능과 동일합니다.

유사점을 비교해보자. 먼저 마이크로프론트엔드는 백엔드 서비스에 대한 마이크로서비스의 역할이 UI 프론트엔드여야 한다는 결합부터 시작하겠습니다. 이 경우 다음이 있어야 합니다.

독립 실행형으로 시작할 수 있습니다

독립적인 URL 제공

독립적인 수명을 가짐(시작, 재활용, 종료)

독립적인 배포 수행

독립 사용자 정의 / 상태 관리

어딘가에서 전용 웹서버로 실행(예: 도커 이미지)

결합하면 게이트웨이와 같은 구조를 사용합니다.

물론 여기 저기에 적용되는 몇 가지 사항은 SPA 구성 및 결과적으로 사이트 없는 UI와도 모순된다는 점에 유의하십시오.

이제 이것을 추측이 다음과 같이 변경될 때 할 수 있는 비유와 비교해 보겠습니다. 사이트리스 UI는 프론트엔드 UI여야 합니다. 이 경우 다음이 있습니다.

시작하려면 런타임 필요

런타임에서 제공한 환경 내에서 URL 제공

런타임에 의해 정의된 수명과 결합됩니다.

독립적인 배포 수행

부분적으로 독립적으로 정의됨, 부분적으로 공유됨(그러나 통제됨/격리됨) 사용자/상태 관리

다른 곳(예: 클라이언트)에서 운영되지 않는 인프라에서 실행

이것이 완벽한 비유처럼 읽힌다는 데 동의한다면 – 굉장합니다! 그렇지 않은 경우 의견 타격에 포인트를 제공하십시오. 나는 이것이 어디로 가고 있는지, 그리고 이것이 따라야 할 좋은 아이디어라고 생각하는지 계속 보려고 노력할 것입니다. 매우 감사!

#마이크로프론트엔드 #클라우드

키워드에 대한 정보 마이크로 프론트 엔드

다음은 Bing에서 마이크로 프론트 엔드 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 [Track 2-4] 원지혁 - 프론트엔드에서의 마이크로 서비스 아카텍처

  • FEConf
  • FEConf2019
  • 원지혁
[Track #2-4] #원지혁 #- #프론트엔드에서의 #마이크로 #서비스 #아카텍처


YouTube에서 마이크로 프론트 엔드 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 [Track 2-4] 원지혁 - 프론트엔드에서의 마이크로 서비스 아카텍처 | 마이크로 프론트 엔드, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *