Skip to content

Commit

Permalink
Merge pull request #25 from KunHwanAhn/release/23.01.0
Browse files Browse the repository at this point in the history
Release/23.01.0
  • Loading branch information
KunHwanAhn authored Apr 5, 2023
2 parents caccd8c + e72af29 commit 8dccb7e
Show file tree
Hide file tree
Showing 122 changed files with 3,686 additions and 40 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# 프로그래머스 프론트엔드 아키텍처 변천사: 좋은 개발 경험을 찾아서
- 김은수 - 프로그래머스
- [발표자료](https://www.slideshare.net/PeterEunsuKim/ss-254802834)

## 프로그래머스 프론트엔드 아키텍처(~2021)
- Ruby on Rails
- Vue + Webpack

### 겪은 문제들
- Vue 코드만 수정했는데 배포하는데 너무 오래 걸려요
- 서로 다른 팀이 하나의 package.json을 공유해서 의존성 관리가 힘들어요
- Rails Webpacker에 강한 의존성이 생겼어요
- => 결론적으로 개발 경험이 안 좋아요

## 프론트엔드 개발자가 원하는 것
- 빠르게 개발하고 배포하는 것
- 의존성 관리도 팀별로 알아서 척척
- Vue말고 React도 쓰고 싶다

> 그래서 우리는 프론트엔드 아키텍처를 개선하기로 했습니다
## New 프론트엔드 아키텍처 설계

### DO
- 프론트엔드를 Rails 애플리케이션과 독립적으로 개발/빌드/배포 가능
- 프론트엔드 코드베이스는 모노레포로 관리
- 전사 공통 패키지 외에는 팀에서 자유롭게 의존성 도입
- 기존 아키텍처와 새로운 아키텍처가 공존하고, 점진적(Progressive)으로 마이그레이션 가능

### DON'T
- 아키텍처를 바꾸느라 제품 개발 프로세스가 멈추면 안됨

## 선행 조사
- Airbnb 사례 - [Hypernova](https://github.com/airbnb/hypernova)
- 오늘의집 사례 - [오늘의집 MSA Phase 1. 프론트엔드 분리작업](https://www.bucketplace.com/post/2021-12-03-%EC%98%A4%EB%8A%98%EC%9D%98%EC%A7%91-msa-phase-1-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%EC%9E%91%EC%97%85/)

### Airbnb 사례 - Hypernova
- 클라이언트 코드가 서버 코드로부터 분리될 수 있음
- 점진적으로 적용 가능함
- 실제로 구현된 사례를 확인함으로써 아키텍처 설계에 대한 힌트를 얻게 되었다

#### Server
```JavaScript
var hypernova = require('hypernova/server');

hypernova({
devMode: true,
getComponent(name) {
if (name === 'MyComponent.js') {
return require('./app/assets/javascripts/MyComponent.js);');
}

return null;
},
port: 3030,
});
```

#### MyComponent.js
```JavaScript
const React = require('react');
const renderReact = require('hypernova-react').renderReact;

function MyComponent(props) {
return <div>Hello, {props.name}!</div>;
}

module.exports = renderReact('MyComponent.js', MyComponent);
```

### New 프로그래머스 프론트엔드 아키텍처 - Core Concept
- 프론트엔드 코드를 저장할 모노 레포지토리
- 프론트엔드 애플리케이션 manifest를 Rails 템플릿에 임베딩
- Webpack Manifest
- Vue / React / Anything in Rails

#### Webpack Manifest
- Webpack으로 빌드된 애플리케이션을 포함하는 코드
- 개발자가 작성한 소스 코드
- 작성한 소스 코드가 의존하는 라이브러리 및 벤더(Vendor)코드
- **Webpack runtime 및 manifest**
- 어떤 컴포넌트를 어떤 방식으로 어떻게 로딩을 해야 하는지와 같은 내용이 정리되어 있음

#### Webpack Manifest Plugin
- Webpack manifest를 추출할 수 있게 해주는 플러그인
- webpack-manifest-plugin => `manifest.json` 파일을 생성함

##### webpack.config.ts
```TypeScript
import { WebpackManifestPlugin } from 'webpack-manifest-plugin';

const productionConfig: Configuration = {
mode: 'production',
output: { filename: '[contenthash].js' },
module: { /*...*/ },
plugins: [
new WebpackManifestPlugin(),
],
};
```

##### dist/manifest.json
```json
{
"main.js": "/hashed-name-1.js",
"175.css": "/175.hashed-name-2.css",
"175.css.map": "/175.hashed-name-2.css.map",
"js": "/hashed-name-3.js",
"js.map": "/hashed-name-3.js.map",
"index.html": "/index.html"
}
```

### 개선된 아키텍처의 장점 - Scalability
- 기존 Rails 코드베이스의 수정 없이 새로운 웹 애플리케이션 개발 및 배포가 가능

### 개선된 아키텍처의 장점 - Independency
- 애플리케이션의 의존성을 독립적으로 관리가 가능

### 개선된 아키텍처의 불편한 점
- 각 애플리케이션에서 의존하는 코드가 새로 릴리즈되면 해당 사항을 반영하기 위해 모든 앱을 다시 배포해야 함

> 앱을 다시 배포하지 않고 의존하는 코드의 변경사항을 런타임에 바로 적용할 수 없을까? => Webpack5 Module Federation
## Webpack5 Module Federation - Core Concept
> NOTE: Docker의 Container가 아니고, 용어임
- 빌드된 애플리케이션 = 컨테이너
- 호스트(Host) 컨테이너: 외부에 노출된 모듈을 불러와 사용
- 리모트(Remote) 컨테이너: 모듈을 외부로 노출(expose)
- Webpack 5버전부터 포함된 Container Plugin을 사용하여 적용 가능
- Container Plugin을 추사오하한 Mudle Federation Plugin을 사용하면 더 쉽게 적용 가능

### Host 컨테이너의 Webpack Config
```TypeScript
import { container } from 'webpack';

const config: Configuration = {
// ...생략
plugins: [
new container.ModuleFederationPlugin({
remotes: {
remoteApp: `remoteApp@${process.env.APP_URL}/remoteEntry.js`,
}
}),
],
};
```

### Remote 컨테이너의 Webpack Config
```TypeScript
import { container } from 'webpack';

const config: Configuration = {
// ...생략
plugins: [
new container.ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Header': './src/components/Header.tsx',
}
}),
],
};
```

### Mudule Federation 사용 예시
```JavaScript
import { Suspense, lazy } from 'react';
import { LoadingSpinner } from '@/components/LoadingSpinner';

const Header = lazy(() => import('remoteApp/Header'));

function CustomHeader() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Header />
</Suspense>
);
}

export default CustomHeader;
```

> 그래서 우리는 얼마나 빠르게 제품을 개발하고 배포할 수 있게 되었을까요?
> 빌드시간: 30~40분 => 5분
> 릴리즈 횟수: 46회 => 173회
> Closed Pull Request: 72 PRs / month => 175 PRs / month
## Next Steps
- Rails 번들러 마이그레이션
- Rails에서 번들러 의존성 제거
- 개발 경험 개선

### Rails 번들러 마이그레이션
- Rails Webpacker를 Shakapacker로 마이그레이션 하여 Webpack 5버전을 사용할 뿐만 아니라 babel 외의 빌드 도구(swc, esbuild)도 사용 가능

### Rails에서 번들러 의존성 제거
- Rails로부터 번들러(Webpacker) 의존성을 분리함으로써 Rails는 API 서버처럼 동작

### 개발 경험 개선
- Remote module의 type checking
- 앱 간의 커뮤니케이션 컨벤션 정의
- Best Practice 탐구

> 프로그래머스의 프론트엔드 개발 경험을 개선하기 위한 모험은 계속됩니다.
## Recap
- Rails와 Vue가 함께 있는 레포지토리에서 프론트엔드 개발 경험이 좋지 않음
- 프론트엔드 코드베이스를 Rails 코드와 분리시켜 독립적으로 개발/빌드/배포할 수 있고 점진적으로 마이그레이션 가능한 아키텍처를 설계
- Airbnb의 Hypernova 사례를 바탕으로
- 신규 프론트엔드 애플리케이션의 Webpack manifest를 Rails 템플릿에 임베딩
- 신규 프론트엔드 애플리케이션 간의 Module Federation을 통한 Runtime Integration
- 개발 경험을 개선하기 위한 노력이 여전히 진행 중
- Rails 번들러 마이그레이션
- Rails에서 번들러 의존성 제거
- 신규 프론트엔드 코드베이스 개발 경험 개선
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## v23.01.0 (2023-04-05)
- [GDG] Devfest Webtech 2022 (임시) (#23)
- [Kakao] if(kakao) 2022 ESG (#24)
- [Kakao] if(kakao) 2022 Frontend (#19)
- [GDG] Devfest 2022 (#20)
- [AWS] Frontend 2022 - 프로그래머스 프론트엔드 아키텍처 변천사 (#18)

## v22.12.0 (2022-12-05)
- [FEconf] FE Conf 2022 영상 주소 수정 (93be04e)

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
Loading

0 comments on commit 8dccb7e

Please sign in to comment.