

하나의 통합된 코드 베이스로 여러 비즈니스 기능을 수행하는 전통적인 아키텍처 스타일. 즉, 단일 애플리케이션 내에 서비스의 모든 로직이 들어가 있는 구조이다. 프로젝트 규모가 작을 때, 빠른 MVP 개발이 필요할 때, 복잡한 비즈니스 로직이 필요하지 않을 때, 변경이 적은 시스템에서 적용되야할 개발 모델이다.
간단한 유지보수 : 단일 코드 베이스를 가지고 있기 때문에 변경 사항을 적용하고 유지보수하는 것이 간단해 코드의 일관성을 유지하고 버그를 수정하기 용이하다.
개발 속도 향상 : 코드베이스가 단순하고 통합이 쉬우므로 초기에 빠르게 개발하고 배포할 수 있다.
통합의 편리함 : 서로 다른 기능이나 모듈이 하나의 애플리케이션 안에 있으므로 통합 및 테스트가 간단하다. 새로운 기능을 추가하거나 변경할 때 다른 부분과의 상호작용을 고려할 필요가 적다.
적은 인프라 구조 비용 : 단일 애플리케이션으로 모든 것을 처리하기 때문에 인프라 구조의 복잡성이 낮아지고 운영 비용이 감소할 수 있다.
기술 스택의 통일성 : 단일 코드 베이스를 가지고 있기 때문에 기술 스택을 통일하기 쉽다.
쉬운 디버깅 및 최적화 : 단일 코드 베이스에서 발생한 문제를 디버깅하기가 비교적 간단하다. 또한, 코드 베이스 전체에 대한 통합된 최적화가 가능하다.
확장과 배포의 어려움 : 애플리케이션 전체를 확장해야 하므로, 특정 기능만을 확장하기 어렵다. 가령 뉴스 서비스를 이용하는 사용자가 1명이고 웹툰 서비스를 사용하는 사용자가 1억명이라 가정했을 때, 웹툰 서비스만 확장하기 어렵다. 작은 변경 사항도 전체 애플리케이션을 다시 배포해야 하는 경우가 있다. 이는 배포의 번거로움과 다운타임을 초래할 수 있다.
기술 스택의 제한성 : 기술 스택을 통일시키기 쉽지만, 이는 향후 새로운 기술 도입이 어렵다는 의미가 된다.
복잡성 증가 : 프로젝트가 성장함에 따라 코드베이스가 커지고 복잡성이 증가할 수 있다. 이에 따라 코드의 일부를 이해하고 수정하는 것이 어려워 유지보수와 디버깅이 어려워질 수 있다.
대규모 팀 작업의 어려움 : 모든 팀이 동일한 코드, 동일한 프로젝트에서 작업하기 때문에 코드 병합에 대한 충돌 가능성이 높고, 기능 변경 시 다른 팀이 작업에 영향을 줄 수 있다.
코드베이스를 여러 개의 Git 저장소로 분리하여 관리하는 방식이다.
종속성 관리의 어려움 : 여러 저장소에서 동일한 라이브러리나 의존성을 사용하는 경우, 버전 관리를 개별적으로 해야 하므로, 일관성을 유지하기 어려울 수 있다.
통합과 테스트 복잡성 : 각 저장소가 독립적으로 관리되기 때문에, 전체 시스템의 통합 테스트가 복잡해질 수 있다. 특히, 저장소 간의 의존 관계가 있을 경우 전체적인 통합 테스트 및 배포가 어려워질 수 있다.
릴리즈 관리의 복잡성 : 여러 저장소를 통해 하나의 애플리케이션을 구성할 때, 각 저장소에서 동시에 배포되어야 하는 경우 일관성 있게 릴리즈 관리하기가 까다로울 수 있다.
CI/CD 파이프라인의 복잡성 증가 : 각 저장소에 대해 별도의 CI/CD 파이프라인을 설정해야 하기 때문에 관리해야 할 파이프라인의 수가 늘어나고, 설정과 유지 보수에 시간과 비용이 더 들게 된다. 하나의 저장소가 업데이트될 때마다 전체 시스템에 대한 통합 배포를 고려해야 하는 경우, 파이프라인의 의존성 관리가 복잡해질 수 있다.
팀 간 협업 문제 : 기능 간 의존성이 높은 경우, 각 팀이 개별 저장소에서 작업하더라도 다른 저장소의 변경 사항을 반영해야 할 수 있어 협업이 복잡해질 수 있다.
코드 검색과 가시성의 제한 : 전체 애플리케이션의 아키텍처와 관계를 한눈에 파악하기 어려워지며, 특정 기능이나 버그 위치를 파악하기가 더 복잡해질 수 있다.
여러 프로젝트나 모듈을 하나의 저장소에서 관리하는 방식이다. 하나의 저장소에 모든 코드가 포함되기 때문에, 여러 프로젝트가 단일 코드베이스 안에서 공존한다. 다양한 프로젝트(예: 프론트엔드, 백엔드, 라이브러리 등)를 하나의 저장소에 모두 담아 관리합니다.
확장성 문제 : 저장소가 커질수록 코드베이스 관리가 어려워지고, 빌드 시간과 CI/CD 파이프라인의 속도가 느려질 수 있다. 대규모 프로젝트의 경우 변경 사항이 많아지면서 충돌이 잦아질 수 있다.
빌드 및 테스트 속도 문제 : 모든 모듈에 대한 빌드를 설정하면 빌드 시간과 테스트 시간이 길어질 수 있다. 변경된 부분만 선택적으로 빌드하거나 테스트하는 설정이 필요하다.
권한 및 접근 관리의 복잡성 : 모든 프로젝트가 하나의 저장소에 포함되어 있기 때문에, 개별 프로젝트마다 다른 접근 권한을 설정하기 어렵다. 팀마다 필요한 프로젝트에만 접근해야 하는 경우, 관리가 복잡해질 수 있다.
Lerna: 자바스크립트와 같은 언어에서 모노레포를 효과적으로 관리하고 빌드할 수 있도록 도와주는 도구
Nx: 모노레포 환경에서 Angular, React, Node.js와 같은 프로젝트를 관리하며, 빌드 최적화와 효율적인 작업을 지원
Bazel: Google에서 개발한 빌드 도구로, 대규모 모노레포 프로젝트의 빌드와 테스트를 효율적으로 수행할 수 있도록 지원
Turborepo: 자바스크립트 기반의 모노레포 관리 도구로, 캐싱과 병렬 처리를 통해 빌드 속도를 향상시키는 데 중점
마이크로 서비스 아키텍처의 원칙을 웹 프론트엔드에 적용한 방식. 웹 프론트엔드를 작은 독립적인 단위로 나누어 개발하고 배포하는 아키텍처 패턴을 의미한다. 대규모 개발팀의 조직 구조에 맞도록 변형된 아키텍처이다. Monolithic와 다르게 레퍼지토리만 하나로 사용할뿐 개별적으로 패키지를 관리하고 빌드/배포가 가능하다.

독립성 및 확장성 : 각 마이크로 서비스는 독립적으로 개발, 배포, 확장이 가능하며, 이는 전체 시스템의 유연성을 향상시킨다. 각 모듈간의 상호 의존성이 낮아지기 때문이다.
기술의 다양성 : 각 서비스는 자체적인 기술 스택을 선택할 수 있어, 최적의 도구나 언어를 사용할 수 있다.

쉬운 유지보수 : 특정 서비스에 대한 다른 서비스에 미치는 영향이 적어서 유지보수가 용이하다.
빠른 배포 : 각 서비스는 독립적으로 배포될 수 있어서 더 빠른 배포 주기를 가질 수 있다.
스케일링의 용이성 : 특정 서비스에 대한 수요가 증가할 때, 해당 서비스만 확장할 수 있어 자원을 효율적으로 사용할 수 있다.
운영의 복잡성 : 여러 서비스 간의 통신과 관리는 복잡성을 증가시킬 수 있다. 서비스 디스커버리, 로깅, 분산 추적 등을 관리해야 한다.
데이터 일관성 : 서비스가 독립적으로 데이터를 가지고 있을 때 일관성을 유지하기 어려울 수 있다.
테스트의 어려움 : 여러 서비스 간의 통합 테스트와 종단 간 테스트를 수행하기 어려울 수 있다.
시스템 전반의 복잡성 증가 : 전체 시스템이 여러 서비스로 이루어지므로 이를 관리하고 이해하는 데 추가적인 노력과 복잡성이 발생할 수 있다.
시스템 전환 비용 : 기존 MA 구조에서 MSA로의 전환은 비용과 시간이 소요될 수 있다.
분산 시스템 문제 : 트랜잭션 처리, 일관성 유지 등 분산 시스템의 특유한 문제에 대한 처리가 부가적으로 필요하다.
개발 후 각 단위 애플리케이션을 어떻게 통합할지 고려해야 한다.

1. Server-side template Composition
여러 개의 Next.js 서버가 있고 각 서버는 특정한 페이지 단위의 애플리케이션을 담당할 때, 사용자가 웹 브라우저에서 특정 URL을 입력하면, 이 요청은 해당 URL에 맞는 서버로 전달됩니다.
Nginx는 웹 서버 역할을 하면서, 사용자가 요청한 URL에 따라 올바른 서버로 요청을 라우팅한다. 그리고 서버에서 HTML과 JavaScript 파일을 생성하여 응답한다. 사용자에게는 모든 것이 하나의 페이지처럼 보이지만, 실제로는 여러 서버에서 각각의 페이지를 동적으로 조합하여 보여주는 것입니다. Route53이나 CloudFront 같은 인프라 서비스를 사용하면, URL에 따라서 요청을 처리할 서버를 쉽게 설정할 수 있다.
2. Build-time Integration
각 마이크로 앱을 npm과 같은 패키지 관리 도구를 이용해 배포한다. 즉, 각 마이크로 앱은 독립적인 라이브러리로 개발되며, 필요한 경우 다른 애플리케이션에서 사용할 수 있도록 패키징된다. 컨테이너 애플리케이션은 이러한 마이크로 앱을 라이브러리 종속성으로 포함한다. 예를 들어, 메인 애플리케이션의 package.json 파일에 해당 마이크로 앱의 패키지를 추가하여 사용할 수 있다. 각 마이크로 앱은 독립적으로 개발되었기 때문에, 필요한 기능을 가져와서 사용할 수 있으며, 이를 통해 전체 웹 애플리케이션을 구성한다. 각 마이크로 앱은 독립적으로 버전 관리가 가능하여, 컨테이너 애플리케이션은 필요할 때마다 특정 버전의 마이크로 앱을 선택하여 사용할 수 있다. 이로 인해 각 마이크로 앱의 변경이나 업데이트가 다른 애플리케이션에 미치는 영향을 최소화할 수 있다. 이 방식은 마이크로 앱의 재사용성을 높여준다. 한번 개발된 마이크로 앱은 여러 개의 다른 애플리케이션에서도 재사용할 수 있으며, 개발자가 중복된 작업을 줄일 수 있다.
3. Run-time Integration via iframes
각 애플리케이션은 서로 다른 기능이나 페이지를 제공하며, 서로 독립적으로 실행된다. 이러한 애플리케이션을 웹 페이지에 표시하기 위해 iframe을 사용합니다. 예를 들어, 메인 웹 페이지에 여러 개의 iframe을 만들어 각 iframe에 다른 애플리케이션을 넣는 방식이다. iframe 안에 들어간 애플리케이션은 외부의 영향을 받지 않고 독립적으로 실행되어 각 애플리케이션이 서로 다른 CSS 스타일이나 JavaScript 코드를 가지고 있어도 서로 충돌하지 않는다. 각 iframe이 독립적으로 실행되기 때문에, iframe 간의 데이터 공유나 UI 통일이 어려운 단점이 있어 서로 간의 정보를 주고받기 위해 추가적인 작업이 필요하다. 주로 서로 완전히 독립적인 기능을 제공하는 애플리케이션에 사용된다.
4. Run-time Integration via web-components
웹 컴포넌트는 캡슐화된 방식으로 동작하여, 외부와의 영향을 최소화한다. 각 단위 애플리케이션을 웹 컴포넌트로 정의합니다. 예를 들어, 버튼, 카드, 모달 등과 같은 UI 요소를 각각 웹 컴포넌트로 만들어 사용할 수 있다. 이들은 HTML 태그처럼 사용될 수 있으며, 각 컴포넌트는 자신만의 스타일과 동작을 포함한다. 이러한 웹 컴포넌트를 사용할 메인 애플리케이션(컨테이너 애플리케이션)에 여러 웹 컴포넌트를 포함하여 UI를 구성한다. 웹 컴포넌트를 사용하면 각 컴포넌트는 독립적으로 실행되며, 다른 컴포넌트와 충돌이 없다. 이를 통해 각 컴포넌트는 자신만의 스타일과 스크립트를 가질 수 있고, 외부의 CSS나 JavaScript에 영향을 받지 않는다.
웹 컴포넌트 방식은 각 컴포넌트가 필요할 때 동적으로 로드되고, 상태를 관리하는 데 있어 React와 같은 프레임워크의 API를 사용할 수 있다. 이를 통해 여러 컴포넌트 간의 데이터 공유나 상태 관리를 쉽게 할 수 있다. 웹 컴포넌트는 표준 HTML 요소로 동작하기 때문에, 다양한 환경에서 사용할 수 있는 유연성을 제공한다.
5. Run-time Integration via javascript
UI 컴포넌트들을 작은 단위로 나누어 각각의 번들로 만든다. 웹 앱에서 사용자가 특정 기능을 요구할 때, 필요한 번들을 동적으로 로드합니다. 번들이 로드되면, 그 안에 정의된 컴포넌트들이 웹 앱의 UI에 추가되어 여러 개의 독립적인 컴포넌트들이 런타임에서 하나의 통합된 앱처럼 작동하게 된다. Context API, Redux 등을 사용하여 각 번들 간의 상태를 쉽게 공유할 수 있습니다. 즉, 여러 컴포넌트가 같은 상태를 필요로 할 때, 이 상태를 손쉽게 관리하고 공유할 수 있다.
이 방식의 가장 큰 장점은 배포 단위를 유연하게 관리할 수 있다는 것이다. 필요에 따라 배포 단위를 쉽게 추가하거나 분리할 수 있으며, 이전에 존재하던 배포 단위와 자연스럽게 결합할 수 있다. 이러한 방식은 Webpack 5에서 제공하는 Module Federation Plugin을 통해 구현된다. 이 플러그인은 여러 개의 분리된 애플리케이션을 정의하고, 런타임에 이들을 합치는 기능을 제공한다.
여러 자바스크립트 애플리케이션에서 코드를 동적으로 공유할 수 있는 기능이다. 여러 자바스크립트 애플리케이션 코드를 동적으로 런타임에 공유하여 MFA 구현을 위한 ‘컴파일 타임’ 종속성을 ‘런타임’ 종속성으로 변환해준다.
webpack.config.js 파일에서 expose를 통해 다른 애플리케이션에 노출할 모듈을 지정하고, remote를 통해 다른 애플리케이션에 가져올 모듈을 지정하는 식으로 사용한다. 이를 통해 런타임에 원격 모듈을 비동기적으로 로드하여, 각 마이크로 앱은 독립적인 빌드 프로세스를 가지면서도, 배포 후에는 동적으로 통합된다.
[아키텍처] 모놀리식 아키텍처 VS 마이크로 서비스 아키텍처(MSA)
MFA (Micro Frontend Architecture)란? 도입 하기
마이크로프론트엔드의 모든 것: 장단점, 도입 시기, 그리고 구현 방법