2025 Toss 개발 컨퍼런스 후기 정리
Native ESM에 올라탄 마이크로 프론트엔드
토스증권의 마이크로 프론트엔드 아키텍쳐(MFA), 어떻게 구현되었을까요? Navtive ESM과 Import Map을 활용해 매우 큰 규모의 코드에서도 Silo별 독립 배포, 쾌적한 개발 환경, 빠른 사용자 경험을 동시에 달성한 과정을 소개합니다.
기존 1개 프로젝트 문제
- 배포시 PR 이 여러개 중복
- 변경 영향도 증가
- 빌드시간 + 배포시간
마이크로 프론트엔드로 전환
도메인 분할 단위를 어떻게 가져갈 것인가?
⇒ Page 단위가 가장 적합했음
런타임 통합
각 페이지별 넘어가는게 많아서 js를 활용한 런타임 통합
SPA 처럼 동적으로 laze하게 불러오는 느낌으로 함
Native ESM
브라우저 ESM → 실행시점에 모듈을 다운로드
import B from "https://exmaple.com/hello.js"
위와같이 불러와서 처리함
해당 js를 보는 시점에 js 값을 불러옴
Import map
다운로드 될 내용을 직접 적용 가능함
<script type="importmap">
{
"imports": {
"@mfa/app-a": "/app-a.js
}
}
</script>
위와같이 불러옴
결국 import map을 어떻게 제공해주느냐가 문제임
⇒ 토스에서는 HTML 에서 서빙함
라우팅 테이블
해당 페이지로 접근했을때 app 을 동적으로 로드함
host app과 client app 간의 결합은 importmap 을 사용함
페이지 이동
보통은 하나의 앱 내에서 처리하는걸 기본으로 함
다만 이경우엔 페이지 이동이 앱 외부로도 가능해야함
이부분은 패스함…ㄷ
이게 노하우일것같은데ㄷㄷ
실제 구현
개별배포가 가능해야함
[이미지 첨부]
각각의 path를 보고 어떤 app을 불러와야하는지를 알 수 있음
라우팅 테이블은 동적으로 HTML을 만들어서 내려줌
배포
- 걱 app이 배포가 되면 ImportMap값을 업데이트하여 새로운 사용자가 다시 올 수 있도록 처리함
- 카라니 배포와 프리뷰 배포도 쉽게 처리가 가능함
- 로컬 환경도 마찬가지로 importmap을 처리하면 가능함
- localhost:3000을 넣으면 됨
번들 사이즈 최적화
공통으로 사용중인것들은 하나로 합침
다른거 하다가 못봤네 ㄷㄷ
언제나, 누구에게나, 평등하게 빠른 웹
‘성능 격차 없는 웹’을 목표로, 다양한 네트워크·디바이스 환경에서도 모든 사용자에게 빠르고 일관된 경험을 제공하기 위한 토스뱅크의 웹 성능 최적화 여정을 소개해요. CDN, Caching, Preload 등 다양한 기술을 활용해 웹의 한계를 극복하려 했던 실전 사례와 그 과정에서의 고민들을 함께 공유합니다.
네트워크 상태나 상황, 기기 등에 따라서 속도 문제가 생김
- 네트워크 변수
- 한 유저가 같은 경험을 하는것도 중요함
- 모두에게 같은 성능 경험
- 동일 네트워크 + 동일 하드웨어 성능
이게 가능한가? 기기가 다르고 네트워크가 다른데…ㄷㄷ
기기에 따라서는 고정변수니 네트워크에 집중함
SSR 방식으로 처리
js를 지연처리하여 화면을 먼저 보여주도록 처리함
마지막에 API dycjd
[나중에 pdf 나오면 캡쳐 넣자 - 크리티컬 랜더링 패스]
클라이언트의 네트워크 통신 상황에 따라 다름
vs 모바일 네이티브랑 비교해서 각각 필요한 리소스를 불러오는데 필요한 값들을 불러오는데 차이가 큼
⇒ 최대한 html, css, js를 가져오는 시간을 줄이자!
CDN 도 그중 하나임
Disk cache를 사용하는것도 괜찮은 방법임
http cache 전략을 잘 사용할것
디자인 시스템은 cdn으로 제공중임. 버전에 따라 다름
JS로직도ㅇㅇ
html에서 캐싱된 리소스를 요청하면 ㅇㅇ
html을 캐싱 안함
⇒ SSR 에서 html을 캐싱하면 문제가 생길 수 있음
클라이언트에서 데이터를 호출하는걸 최소화해서 처리하였음 ⇒ 서버에서 API 데이터까지 불러와서 처리함
stale while revalidate (SWR)
다음 버전의 정보를 미리 백드라운드에서 가져옴
최초 접근일때만 네트워크를 받아옴
항상 최초 화면을 보여줘야하는 경우엔 좋은 방법은 아닐 수 있음
payload
service worker 를 활용
⇒ 다양한 서비스를 제공하고 있는 곳에선 좀 어려웠음
App을 통해 Preload 사용
⇒ 어떤 리소스를 preload 를 할지 고민
- 유입이 많은 페이지?
- 첫 진입
preloadPages = ["/index", "/???"]
preload list 에 js와 css를 따로 저장함
앱이 원하는 타이밍에 preload list 를 가져와서 미리 캐싱을 해둠.
CDN 요청수가 너무 많음
신규 배포가 없는경우엔 CDN preload 를 하지 않도록 처리함
⇒e-tag
진입 가능성이 있는 유저만 preload 하도록 처리
- 진입 경험이 있는 사람만 사용
daily preload
wifi 무제한 사용자?
이걸 어떻게 알지?
스토리지 사이즈
여러가지 요소를 보고 preload 할지 여부를 확인함
지표
FCP
얼마나 빠르게?
안쓰는 이유
- 너무 잦은 배포로 preload 시간이 없음
- CDN 요청 수가 너무 많음
더 나은 UX를 위한 프론트엔드 전략
토스뱅크가 더 나은 UX를 만들기 위해 어떤 노력을 해왔는지 소개하고자 해요. 누구나 할 수 있는 작은 시도들이 모여 어떻게 큰 사용자 경험의 개선으로 이어졌는지, 그리고 그렇게 만들어낸 UX를 꾸준히 지켜가기 위해 선택한 전략까지 함께 공유합니다.
로딩속도 + 안정성
로딩속도
SSR 활용
- 네트워크 구간
- 스크립트 구간
- 데이터 페칭 구간
SSR을 통해 html을 그려서 내려줘서 초기 로딩 속도가 개선되긴 함
어떤 영역을 SSR로 그릴것인가?
⇒ 금액 영역만 보기로 결정함
- 비즈니스적 측면
- 금액을 가징 먼저 보고싶음
- 기술적 측면
- 거래내역 API 응답까지 너무 오래걸림
- 브라우저에 내려줄 HTML 사이즈가 너무 커질 수도 있음
거래내역은 클라이언트사이드 캐싱을 하기로함
localstorage 에 저장함.
두번쨰 진입부터 차이남
localstorage 에서 먼저 가져오고, ㅇㅇ
번들 사이즈
js 평가 시간을 줄이기 위한 노력임
- 미사용 의존성 제거
- 용량 큰 라이브러리는 작은 라이브러리로 대체
- 같은 라이브러리지만 버전이 다른것을 정리
이거 옛날에 slash21 에 있는 js bundle 다이어트 참고
API waterfall 개선
API가 하나씩 feching
suspense 를 통한 처리
useSuspenseQuery 를 잘못하용하면 직렬로 API 를 호출하게 됨
⇒ useSuspenseQueries 를 사용하는게 나음
화면 안정성
천천히 보면 보이는 것들이 있음
-
금액이 다 보이고 깜빡임
- 서버에서는 금액이 다 보임
- 애니메이션이 진행되면서 안보였다가 fade in
-
카드 아이콘이 깜빡임
- 클라에서 이미지를 결정했음.
- 서버사이드에서 이미지를 내려주도록 처리
-
보내기 버튼 깜빡임
- 송금 가능 여부에 따라서 enable / disable 처리를 하는중
- 데이터를 가져오면서…ㅇㅇ
- ⇒ 송금 가능한 사람이 더 많으니 enable 을 기본값으로 처리
info
이건 트레이드 오프인듯
:::
-
광고배너의 레이아웃 시프트
- 광고배너는 클라이언트 랜더링
- 시간이 지나먼 바뀌어야하기에 cache 처리 불가 → 사용 내역보다 빠르게 노출이 불가능함
- ⇒ 디자인적으로 해결
info
이거 뭘 해결했다는거지?ㄷㄷ
이해를 못함
:::
LCP 최적화
LCP는 로딩 끝났다고 느끼는 시점
-
특정 API가 느린 문제
⇒ 동기적으로 호출하는게 문제임
-
API 응답 시간이 느렸음
- API 병렬 호출 제한
- http 1.0 을 사용했음
-
SSE을 활용 못했음
성능 개선 전략
aggregation API 도입함
⇒ 호출 갯수를
서버 fetching 실패하면 client 에서 refetch 하도록 처리하고 있음
상단 노출되는 부분만 SSR 하고, 아래쪽은 CSR 을 사용함
이정도로까지 해야되는구나;;
이후 대응
변경 이력 관리
- 잦은 실험으로 인해 ㅇㅇ
계속해서 기록을 남기고 어떻게 되었는지 확인함
LCP 를 기준으로 ㅇㅇ
자동화된 LCP 모니터링
매주 LCP 성능 체크 하고 알림 시스템
이상 발생 시 즉시 분석하여 원인 파악
