Micro Frontends 번역글 3/5

Juyeon Ma
12 min readAug 2, 2019

--

이 글은, https://martinfowler.com/articles/micro-frontends.html 페이지를 번역한 글 입니다.

저는 번역 경험이 거의 없습니다. 서투른 글이라 번역체의 분명하지 않은 문장 표현들이 종종 있겠습니다만.. 너그러이 보아 주셨으면 좋겠고, 얼마든지 개선점이나 조언 주시면 감사하겠습니다. 저는 이 문서의 내용이 분명 우리에게 도움이 될 것이라 생각하고, 많은 분들과 나누고 싶습니다.

(그리고 문서가 좀 깁니다. 따로 편집하거나 요약하지 않았습니다.)

미흡한 자료 보아 주셔서 감사합니다 :)

Contents

(이 페이지에서 볼 내용 끝에 * 로 표시)

• 이점 (Benefits)

⁃ 점차적인 업그레이드 (Incremental upgrades)
⁃ 단순하고 분리된 코드베이스 (Simple, decoupled codebases)
⁃ 독립적인 배포 (Independent deployment)
⁃ 자율적인 팀 (Autonomous teams)
⁃ 간단히 말해서 (In a nutshell)

• 예제 (The example)

• 통합에 대한 접근 방식 (Integration approaches)

⁃ 서버사이드 템플릿 구성 (Server-side template composition)
⁃ 빌드 타임 통합 (Build-time integration)
⁃ iframe을 통한 런타임 통합 (Run-time integration via iframes)
⁃ JavaScript를 통한 런타임 통합 (Run-time integration via JavaScript)
⁃ 컴포넌트를 통한 런타임 통합 (Run-time integration via Web Components)

• 스타일링 (Styling) *

• 공유컴포넌트 라이브러리 (Shared component libraries) *

• 어플리케이션 간 통신 (Cross-application communication) *

• 백엔드 통신 (Backend communication) *

• 테스팅 (Testing) *

• 상세 예제 (The example in detail)

⁃ 컨테이너 (the container)
⁃ 마이크로 프론트엔드 (The micro frontends)
⁃ 라우팅을 통한 어플리케이션 간 통신 (Cross-application communication via routing)
⁃ 공통 컨텐츠 (Common content)
⁃ 인프라 (Infrastructure)

• 단점 (Downsides)

⁃ 페이로드 크기 (Payload size)
⁃ 환경의 차이 (Environment differences)
⁃ 운영 및 거버넌스 복잡성 (Operational governance complexity)

• 결론 (Conclusion)

스타일링

언어로서의 CSS는 전통적으로 모듈시스템, 네임스페이스나 캡슐화(encapsulation)없이, 본질적으로 전역적이며, 상속되고, 순차적입니다. 이러한 기능 중 일부는 현재 존재하긴 하지만, 브라우저의 지원은 다소 부족합니다. Micro Frontends 환경에서 이러한 문제들 중 많은 부분이 악화됩니다.

예를 들어 한 팀의 micro app에 stylesheet h2 { color: black; }가 있고, 다른 하나의 stylesheet에 h2 { color: blue; }있을 때, 이 두 selector가 한 페이지에 attach된다면 한 쪽은 좌절할 겁니다. 이것이 결코 새로운 문제는 아니지만, 이같은 셀렉터들이 각각 다른 시간, 다른 팀에 의해 작성되고, 코드가 별도의 repo로 분할되면서 서로 발견하기가 어려졌기 때문에 이러한 문제가 한층 더 악화됩니다.

지난 몇 년 동안, CSS를 보다 쉽게 ​​관리할 수 ​​있는 많은 접근법이 개발되었습니다. BEM 과 같은 엄격한 명명 규칙을 사용하여 셀렉터가 의도한 대로만 적용되도록 하기도 합니다. 개발자에게 규율하는 것에만 의존하지 않으려는 다른 사람들은, 셀렉터를 감싸(nesting) 네임스페이스같은 형태로 사용할 수 있는 SASS 같은 전처리기(preprocessor)를 사용합니다.

더 새로운 접근법은, CSS 모듈 또는 다양한 CSS-in-JS 라이브러리를 사용하여 모든 스타일을 프로그래밍 방식으로 적용하는 것입니다. 이는 스타일이 개발자가 의도하는 곳에서만 직접 적용되도록 합니다. 또는, 좀더 플랫폼 기반 접근방식으로, shadow DOM 또한 스타일 분리 기능을 제공합니다.

개발자가 서로 독립적으로 스타일을 작성할 수 있는 방법을 찾고, 코드가 단일 어플리케이션으로 함께 구성될 수 있을 때, 코드가 의도한대로 작동할 거라는 확신만 있다면 그것이 무엇이든 선택한 방식은 그다지 중요하지 않습니다.

공유 컴포넌트 라이브러리

위에서 언급한 것처럼 micro app 간의 시각적 일관성은 중요합니다. 이를 위한 한가지 방법은, 재사용 가능한 공유 UI Component library를 개발하는 것입니다. 이것은 일반적으로 잘하기는 어렵지만 좋은 생각입니다. 이러한 라이브러리를 만드는 주요 이점은 코드 재사용과 시각적 일관성, 이를 통한 노동력 감소입니다. 또한 Component library는 실제로 스타일가이드의 역할을 할 수도 있으며, 개발자와 디자이너 간의 협업을 가능하게 합니다.

가장 발생하기 쉬운 문제점 중 하나는, 이러한 컴포넌트를 너무 일찍, 너무 많이 만드는 것입니다. 모든 애플리케이션에서 필요할, 모든 공통 디자인을 가지고 기초 프레임워크를 만들고 싶겠지만, 실제 사용 전에 컴포넌트의 API를 추측하는 것은 불가능하지는 않지만 어렵다는 것을 경험을 통해 알고 있으며, 이는 컴포넌트의 생성 초기에 많은 변화를 가져옵니다.

이러한 이유로 우리는 팀이 초기에 일부 중복을 발생시키더라도 필요에 따라 코드베이스 내에 자체 컴포넌트를 만드는 것을 선호합니다. 공통된 패턴이 자연스럽게 나타나도록 하고, 컴포넌트의 API가 명확해지고 나면, 공유 라이브러리에서 중복 코드를 추출해낼 수 있고, 확실히 입증된 것들만을 가지고 있음을 확신할 수 있습니다.

공유할 수 있는 가장 확실한 후보는 icons, labels, buttons과 같은 “멍청한(단순한)” 기본적이고 시각적인 엘리먼트들입니다. 또한 자동완성, 드롭다운 검색필드와 같이 상당한 양의 UI 로직을 포함하는 더 복잡한 컴포넌트들을 공유할 수도 있습니다. 정렬, 필터링, 페이징 기능이 있는 테이블도 이에 속합니다.

그러나 이러한 공유컴포넌트에는 비즈니스로직이나 도메인로직 없이 UI로직만 포함되도록 주의하십시오. 도메인로직을 공유 라이브러리에 넣게 되면 어플리케이션 간에 높은 수준의 커플링이 발생되어 변경이 어려워집니다. 예를 들어, “ProductTable" 는 공유하지 않아야 합니다. 그것은 확실히 “Product” 와, 관련하여 어떻게 동작할지에 대한 모든 종류의 가정이 있을 것입니다. 이러한 도메인 모델링 및 비즈니스로직은 공유 라이브러리가 아닌 micro app의 코드에 속해야 합니다.

공유된 내부 라이브러리와 마찬가지로, 소유권과 관리에 대한 몇가지 까다로운 질문이 있습니다. 이 모델은 공유 자산으로서 “모든 사람”이 소유하고 있다고 말하지만, 일반적으로, 실제로는 아무도 소유 하지 않는다는 뜻입니다. 그것은 금방이라도 명확한 컨벤션이나 기술적 비전이 없는, 일관되지 않은 코드의 잡동사니가 될 수 있습니다.

다른 극단에서는, 공유 라이브러리의 개발이 완전히 중앙집중화되면, 컴포넌트를 만드는 사람들과 이를 사용하는 사람들이 크게 단절될 것입니다. 우리가 본 최고의 모델은, 누구나 라이브러리에 기여할 수 있지만 품질, 일관성, 유효성을 보장할 책임이 있는 담당자(해당 개인 또는 팀)가 있는 것입니다. 공유 라이브러리를 유지 관리하는 일은 수준높은 기술력을 필요로 하지만, 또한 많은 팀들 간의 협업을 수행하는 데 필요한 인적 기술도 필요로 합니다.

어플리케이션 간 통신

Micro Frontends 에 관한 가장 일반적인 질문 중 하나는 서로 소통할 수 있게 하는 방법에 대한 것입니다. 일반적으로 우리는 가능한 한 적게 의사 소통을하게 하는 것을 추천합니다. 애초부터 피하고 싶었던 부적절한 커플링을 다시 일으키게 되기 때문입니다.

그렇긴 하지만, 일부 어플리케이션 간 통신은 종종 필요합니다. Custom events 사용하면 micro app이 간접적으로 통신할 수 있으므로 직접 연결을 최소화하는 좋은 방법이지만, 이는 micro app간에 존재하는 규율을 결정하고 강요하는 것을 더 어렵게 만듭니다.

대안으로, 콜백과 데이터를 아래로 전달하는 (이 경우 Container app에서 Micro app으로) React 모델은 전달방식에 대한 규율을 보다 명확하게 만드는 좋은 솔루션입니다.

세 번째 대안은 주소 표시줄을 통신 메커니즘으로 사용하는 것 입니다. 자세한 내용은 나중에 자세히 설명 합니다.

어떤 접근 방식을 선택하든, micro app이 서로 메시지나 이벤트를 보내고, 공유상태는 유지하지 못하게 해야 합니다. Micro Service에서 Database를 공유하는 것 만큼, 데이터 구조와 도메인 모델을 공유하자마자 수많은 coupling이 발생하며, 이는 변경하기가 극도로 어려워집니다.

스타일을 적용할 때와 마찬가지로, 여기에서 잘 작동하는 몇 가지 다른 접근 방식들이 있습니다. 가장 중요한 것은, 어떤 종류의 결합을 도입하려 하고 있는지, 그리고 시간이 지남에 따라 그 계약을 어떻게 유지할 것인지에 대해 오랫동안 깊이 생각해 보는 것입니다. MicroService 간의 통합과 마찬가지로, 여러 어플리케이션과 팀 간의 조정된 업그레이드 프로세스를 거치지 않고서는 통합을 변경할 수 없어야 합니다.

통합이 중단되지 않았다는 것을 자동으로 검증하는 방법에 대해서도 생각해 봐야 합니다. 기능테스트는 이를 위한 하나의 접근 방식이지만, 우리는 구현 및 유지 보수 비용으로 인해 작성해야 하는 기능 테스트의 수를 제한하고 싶어합니다. 또는 Consumer Driven Contracts의 일부 형태를 구현하여, 각 micro app이 실제로 모든 것을 브라우저에서 통합하고 실행할 필요 없이 다른 micro app에게 무엇이 필요한 지 지정할 수 있습니다.

백엔드 통신

프론트엔드 애플리케이션에서 독립적으로 작업하는 팀이 있다면 백엔드 개발은 어떨까요? 우리는 비주얼 코드에서 API 개발, 데이터베이스 및 인프라 코드에 이르기까지 애플리케이션 개발을 담당하는 풀스택 팀의 가치를 매우 믿습니다.

여기에서 도움이 되는 패턴 중 하나는 BFF (Back-end for Front-end Pattern) 패턴입니다. 각 프론트엔드 애플리케이션은 오로지 프런트엔드의 니즈를 충족하기 위한 목적만을 가진 해당 백엔드를 가지고 있습니다. BFF 패턴은 원래 각 프론트엔드 채널 (웹, 모바일 등)에 대한 전용 백엔드를 의미했을 수도 있지만, 각 Micro Frontends Application에 대한 백엔드를 의미하도록 쉽게 확장 될 수 있습니다.

여기에는 고려해야 할 많은 변수가 있습니다. BFF는 자체적인 비즈니스 로직 및 데이터베이스를 포함하고 있거나 다운스트림 서비스의 집합체일 수도 있습니다. 다운스트림 서비스가 있는 경우, Micro Frontends와 해당 BFF를 소유 한 팀이 그러한 서비스의 일부도 소유하는 것은 어쩌면 말이 안될 수도 있습니다.

Micro Frontend Application이 단 하나의 API만을 가지고 있고, 그 API가 상당히 안정적이라면, BFF를 구축하는 데는 큰 가치가 없을 수도 있습니다. 여기서 의 지침 원칙은 특정 micro app을 구축하는 팀이, 다른 팀이 자신들을 위해 무언가를 만들 때까지 기다릴 필요가 없어야 한다는 것입니다. 따라서 micro app에 추가된 어떠한 새로운 기능이 백엔드 변경을 필요로 한다면, 이는 동일한 팀이 BFF를 소유했을 때 강점으로 나타납니다.

그림 7 : 프론트엔드 / 백엔드 관계를 구조화하는 여러 가지 방법이 있습니다.

또 다른 일반적인 질문은 micro app의 사용자가 서버에 어떻게 인증하고 권한을 부여받는지에 대해서입니다. 분명히 고객은 한번만 인증하면 되므로, 인증은 일반적으로 container app이 소유해야 합니다. 컨테이너에는 일종의 로그인 양식이 있고, 이를 통해 일종의 토큰을 얻을 수 있습니다. 이 토큰은 컨테이너가 소유하며, 초기화시 각 micro app에 주입 될 수 있습니다. 마지막으로 micro app은 요청과 함께 토큰을 서버로 보낼 수 있으며, 서버는 필요한 모든 유효성 검사를 수행 할 수 있습니다.

테스트

테스트와 관련하여 monolithic frontends 와 micro frontends 사이에는 별 차이가 없습니다. 일반적으로 monolithic frontends를 테스트하는 데 사용하는 전략은 각 개별 micro frontends에서 재현 할 수 있습니다. 즉, 각 micro frontends application에는 코드의 품질과 정확성을 보장하는 자체 자동화 테스트 세트가 있어야 합니다.

분명한 차이점은 다양한 micro app을 container app과 통합 테스트하는 것입니다. 이는 기능/E2E 테스트 툴(Selenium이나 Cypress와 같은) 중 원하는 것을 사용하여 수행 할 수 있지만 너무 멀리 가지는 마십시오. 기능 테스트는 테스트 피라미드의 하위 레벨에서 테스트 할 수 없는 측면만을 포함해야 합니다. 즉, 단위 테스트를 사용하여 저수준의 비즈니스 로직 및 렌더링 로직을 처리한 다음, 기능 테스트를 사용하여 페이지가 올바르게 조립되었는지 확인해야 합니다. 예를 들어, 특정 URL에 완전히 통합된 어플리케이션을 로드하고, 해당 micro app의 하드코딩된 제목이 페이지에 올바르게 있음을 확인할 수 있습니다.

마이크로프론트엔드에 걸쳐있는 사용자의 여정이 있다면 기능 테스트를 사용하여 커버할 수 있지만, 기능 테스트는 각 micro app의 내부 비즈니스 로직이 아니라 프론트엔드의 통합을 검증하는 데에 초점을 맞추어야 합니다. 위에서 언급했듯이, consumer-driven contracts는 통합된 환경과 기능테스트의 취약성 없이 마이크로프론트엔드 간에 발생하는 상호작용을 직접적으로 명시하는 데 도움이 될 수 있습니다.

다음 글에 이어서…

--

--