안녕하세요.
와이즈트래커의 디자인을 책임지고 있는 앙드라이입니다.
Product owner로서 프로덕트의 방향성도 함께 책임지고 있어요.
프론트 개발도 겸하고 있다보니 자연스럽게 디자인과 프론트의 연결성을 중요하게 샹각하고 있습니다.
그래서 도입한 것이 프로덕트 디자인 툴 Framer입니다.
https://www.framer.com/
토스(Toss)에서 Framer 를 도입하며 화제가 되기도 했죠. (링크)
와이즈트래커는 웹사이트 디자인과 대시보드 디자인, 두 가지 프로덕트에서 Framer를 활용하고 있습니다.
그 중에서도 웹사이트는 디자인부터 배포까지 Framer 단독으로 활용중이예요.

Flex 레이아웃을 기반으로 디자인을 해두면 Media query 적용을 쉽게 할 수 있어 꽤 빠르게 프로덕트를 만들어낼 수 있습니다.
특히 transition 과 keyframe 을 기반으로 하는 애니메이션 처리는 프로덕트의 완성도를 더욱 높여주죠.
Elements 로 구성되는 DOM 구조를 잘 아는 디자이너가 사용하면 아주 수준 높은 프로덕트를 만들 수 있습니다.
바꿔 말하면 일반 크리에이터 범주에 속한 디자이너들에겐 쉽지 않은 툴이기도 해요.
이렇게 디자인툴을 통해 배포까지 할 수 있는 Framer 는 head 나 footer 에 script 를 넣을 수 있기도 합니다.
Amplitude나 Google Analytics와 같은 트래커들을 삽입할 수 있는데요.
한 가지 고려해야 할 사항은 Framer 는 React 기반의 웹사이트가 결과물로 배포된다는 점입니다.
디자인을 하고 배포하게 되면 캔버스에 구성해 둔 이미지들을 React 코드로 컴파일 하고 빌드하여 그들의 서버에 정적 파일들로 서빙된다는 의미예요.
(사실 디자인 하는 모든 엘리먼트들은 실시간으로 React Component 로 구성되어집니다.)
즉, 단일 html에 render 부분을 javascript가 처리하게 되다보니 페이지간의 이동을 캐치하는게 쉽지 않아집니다.
이 글을 작성하는 이유기도 한데요.
방법에 대해 공유해드리면 좀 더 쉽게 접근하실 수 있지 않을까 싶은 마음입니다.
코드와 함께 설명을 함께 곁들일텐데요.
샘플 코드는 와이즈트래커만 포함되어있지만, 그리 어려운 구조는 아니라서 원하는 트래커들은 쉽게 추가하실 수 있을것입니다.
우선 코드 컴포넌트를 만들어주세요.

그리고 다음의 코드를 만들어진 컴포넌트의 코드를 모두 삭제하고 넣어주세요.
import React, { useEffect, useRef } from "react"
import type { ComponentType } from "react"
export function useState(Component): ComponentType {
return (props) => {
const url = window && window.location && window.location.href
const subdirectoryMatch = url.match(/\/\/[^/]+(\/[^/]+)(\/[^/]+)?/)
const returnScreen = (page) => {
switch (page) {
case "/attribution":
screen = {
page_id: "어트리뷰션",
event: "view_product_attribution",
}
break
}
return screen
}
if (subdirectoryMatch && subdirectoryMatch[1]) {
const screenData = returnScreen(subdirectoryMatch[1])
WDOT.logScreen(screenData)
console.log("Framer not main")
} else {
screen = {
page_id: "홈화면",
event: "w_view_home",
}
WDOT.logScreen(screen)
console.log("Framer main")
}
return <Component {...props} />
}
}
적용하려는 페이지에 Code overrides 를 다음과 같이 설정해줍니다.

코드를 다시 한 번 살펴보도록 하죠.
스크린 자체에 적용 한 Global file 의 useState 함수는 페이지가 바뀔때마다 새롭게 로드가 됩니다.
즉, Framer 로 만들어진 모든 페이지에 Global filer 의 useState 함수를 오버라이드 하자는 의미예요.
그렇다면 모든 페이지를 왔다갔다 할때마다 아래의 코드가 불려질 것입니다.
그리고 아래 코드의 주석을 참조해주세요. // 주석은 이것입니다.
import React, { useEffect, useRef } from "react"
import type { ComponentType } from "react"
export function useState(Component): ComponentType {
return (props) => {
const url = window && window.location && window.location.href
const subdirectoryMatch = url.match(/\/\/[^/]+(\/[^/]+)(\/[^/]+)?/)
const returnScreen = (page) => {
switch (page) {
case "/attribution":
screen = {
page_id: "어트리뷰션",
event: "view_product_attribution",
}
break
}
return screen
}
if (subdirectoryMatch && subdirectoryMatch[1]) {
const screenData = returnScreen(subdirectoryMatch[1])
WDOT.logScreen(screenData)
console.log("Framer not main")
} else {
screen = {
page_id: "홈화면",
event: "w_view_home",
}
WDOT.logScreen(screen)
console.log("Framer main")
}
return <Component {...props} />
}
}
그리 어려울게 없죠?
좀 더 쉽게 단락으로 설명하자면 아래의 코드를 switch 하위에 반복 설정하면 됩니다.
case "/attribution":
screen = {
page_id: "어트리뷰션",
event: "view_product_attribution",
}
break
switch 는 returnScreen 이라는 함수 내에서 케이스별로 결과를 반환해주는 녀석이고, returnScreen 은 page 를 인자로 받고 있죠.
아래 코드에서 returnScreen 에 subdirectoryMatch[1]을 인자로 전달하는 모습 보이시나요?
전달하여 리턴 받은 screen 객체(object)는 screenData에 할당되는 모습입니다.
if (subdirectoryMatch && subdirectoryMatch[1]) {
const screenData = returnScreen(subdirectoryMatch[1])
WDOT.logScreen(screenData)
console.log("Framer not main")
} else {
screen = {
page_id: "홈화면",
event: "w_view_home",
}
WDOT.logScreen(screen)
console.log("Framer main")
}
위 코드의 if 문은 메인페이지냐 아니냐를 구분짓고 WDOT.logScreen을 실행하죠.
WDOT.logScreen은 와이즈트래커의 스크린 로깅 태깅입니다.
WDOT.logScreen 과 함께 Amplitude나 Google Analytics 4를 태깅해준다면 함께 분석할 수도 있죠.
amplitude.logEvent("pageview", {
page_name: screenData.page_id,
})
gtag("event", "page_view", {
page_location: subdirectoryMatch[1],
page_title: screenData.page_id,
})
어떠셨나요?
다음 포스트에선 Framer에서 클릭 이벤트 수집하는것을 다뤄볼까 합니다.
고맙습니다.