pile·
프론트엔드·oliveyoung올리브영 테크블로그·

프레임워크에 구애받지 않는 통합 백오피스 구축하기

올리브영 카탈로그 플랫폼 팀이 React/Next, Vue, 레거시 JSP로 갈린 백오피스들에 동일한 상품정보 화면을 제공해야 하는 상황을 Vue 웹 컴포넌트로 푼 사례다. 같은 API를 바라보고 같은 목적을 가진 화면인데 프레임워크가 다르다는 이유로 같은 UI와 로직을 최대 세 벌씩 만들어야 했다. iframe과 시스템 마이그레이션을 검토한 뒤, 모노레포에서 빌드한 웹 컴포넌트를 CDN으로 서빙해 어떤 환경에서든 커스텀 태그로 로드하는 구조를 택했다.

핵심 포인트
  • iframe은 Cross-Origin 정책 때문에 부모 창과 쿠키·세션을 공유하지 못해, 이미 로그인한 화면 위에 얹어도 iframe 안에서 다시 로그인을 요구받는다.
  • 시스템 마이그레이션은 화면 하나를 공유하자고 JSP 레거시 전체를 다시 짜야 해서 배보다 배꼽이 큰 선택이었다.
  • Vue 3의 defineCustomElement로 컴포넌트를 브라우저 표준 커스텀 태그로 변환하면 프레임워크 종속 없이 부모의 인증·자원을 그대로 공유한다.
  • 공통 화면 수정은 모노레포에서 빌드해 S3의 JS 파일만 덮어쓰면 되고 부모 시스템의 재빌드나 배포가 필요 없다.
  • CDN에 올라간 JS는 껍데기뿐인 렌더링 로직이고, 실제 key나 Axios 인스턴스는 부모 시스템의 메모리에서만 런타임에 주입된다.
상세 정리
  • 아키텍처 흐름: 모노레포에서 상품 관리 메뉴를 독립 패키지로 관리하고, Vue 패키지를 프레임워크 종속 없는 순수 JS로 빌드해 S3에 올린 뒤 CDN과 연결하고, 각 서비스는 script 태그로 그 URL을 불러와 커스텀 태그를 선언한다.
  • React 탑재 방식: 미리 작성한 useWebComponent 훅에 ref와 pageName을 넘기고 isDefined로 로딩 상태를 감싸면 커스텀 태그 한 줄로 화면이 붙는다.
  • Shadow DOM 문제: Vue 웹 컴포넌트는 빌드 시 스타일을 전역 style 태그로 주입하는데 컴포넌트는 Shadow DOM이라는 독립 캡슐 안에서 동작해 전역 스타일이 적용되지 않는다.
  • 훅이 하는 일: 전역에 흩어진 Vue 전용 스타일만 골라내 ShadowRoot로 옮기고, 빌드 결과물 CSS는 CDN URL에서 직접 받아오되 응답이 정상이 아니면 에러를 던져 스타일 누락을 명확히 잡는다.
  • 주입 순서가 곧 우선순위라 기본 스타일을 앞에 깔고 CDN에서 받은 컴포넌트 CSS를 뒤에 붙인다. customElements.whenDefined로 커스텀 태그가 정의될 때까지 기다린 뒤 ShadowRoot가 생기면 스타일 주입을 실행하고, 끝나면 로딩 상태를 해제해 깜빡임을 가린다.
  • 독립 배포의 조건: 새로고침만으로 최신이 보장되지는 않는다. CDN 엣지 노드가 TTL에 따라 캐싱하므로 파일을 덮어써도 만료 전까지 구버전이 응답될 수 있다.
  • 해결책으로 CDN 요청 URL에 현재 날짜·시간을 쿼리 파라미터로 붙이는 캐시 버스팅을 썼고, 파일명에 content-hash를 넣거나 배포 시 CDN Invalidation을 호출하는 방법도 함께 고려할 수 있다고 밝힌다.
  • 인증 시나리오 1 쿠키·헤더 토큰: 웹 컴포넌트가 부모와 같은 오리진에서 실행되므로 SameSite로 인한 쿠키 차단 없이 기존 인증 체계를 그대로 쓴다. 다만 CSRF 방어는 부모 시스템 차원에서 별도로 갖춰져 있어야 하고, 이 시나리오는 동일 오리진 실행이라는 전제에서만 성립한다.
  • 인증 시나리오 2 인스턴스 주입: 부모가 API 호출에 쓰는 Axios 인스턴스 자체를 프로퍼티로 통째로 넘겨받아 그대로 호출하면 이미 헤더에 세팅된 인증 정보가 자동으로 실린다.
  • 인증 시나리오 3 이종 도메인: 서버 간 약속된 토큰을 프로퍼티로 넘겨받아 컴포넌트 내부에서 헤더에 동적으로 주입한다.
  • 보안 효과: Axios 인스턴스 주입 방식에서는 누군가 CDN 주소를 탈취해 자기 페이지에 임베드해도 부모 인스턴스 없이는 어떤 데이터에도 접근할 수 없다.
  • 완성도 포인트 1: Shadow DOM 스타일 격리는 어떤 부모에 얹어도 충돌하지 않는다는 강점이지만 전역 스타일이 캡슐에 들어오지 못하므로 주입 로직을 공통 훅으로 한 번만 만들어 두면 이후 신경 쓸 일이 거의 없다.
  • 완성도 포인트 2: 커스텀 태그가 정의된 뒤 렌더링되는 특성상 SSR이나 검색 노출이 중요한 공개 화면보다 로그인 뒤 동작하는 백오피스·어드민에 잘 맞고, 적용 대상이 정확히 그 영역이라 걸림돌이 아니었다.
  • 완성도 포인트 3: CDN에서 스크립트를 받아 태그가 정의되기까지의 짧은 지연은 로딩 처리로 가리면 사용자가 거의 체감하지 못한다.
  • 완성도 포인트 4: defineCustomElement 동작은 Vue 버전에 따라 달라질 수 있지만 모든 채널이 하나의 모노레포에서 빌드된 컴포넌트를 소비하므로 버전을 한곳에서 일괄 관리할 수 있어, 파편화된 환경보다 오히려 통제가 수월하다.
  • 자평: 웹 컴포넌트가 프론트엔드 생태계에서 보편적인 기술이 아니라 고민과 의구심이 많았고 최고의 정답이라 단언할 수는 없으나 당시 제약에서는 최선의 선택이었다고 정리한다. 레거시를 리스크 없이 점진적으로 모던화할 발판을 마련한 것을 가장 큰 수확으로 꼽는다.
왜 읽나프레임워크가 갈린 여러 어드민에 같은 화면을 얹어야 하는 프론트엔드 팀이, iframe의 인증 한계를 우회하면서 독립 배포까지 얻는 대안과 그 대가로 챙겨야 할 항목을 확인할 수 있다.
oliveyoung
올리브영 테크블로그 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2