Next.js: GA 슀크립트 νƒœκ·Έλ₯Ό μΆ”κ°€ ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?

에 λ§Œλ“  2016λ…„ 10μ›” 30일  Β·  74μ½”λ©˜νŠΈ  Β·  좜처: vercel/next.js

μ•ˆλ…•!

ν˜„μž¬ λ‚΄ μ›Ή μ‚¬μ΄νŠΈλ₯Ό next.js ν”„λ‘œμ νŠΈλ‘œ μ „ν™˜ 쀑이며 νŽ˜μ΄μ§€μ— Google μ• λ„λ¦¬ν‹±μŠ€ 슀크립트 νƒœκ·Έλ₯Ό μΆ”κ°€ν•˜λŠ” 방법을 μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. μ΄κ²¬μžˆλŠ” μ‚¬λžŒ?

건배,
λ‚¨μž 이름

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

react-ga ν•„μš” μ—†μŠ΅λ‹ˆλ‹€. <Head> ꡬ성 μš”μ†Œμ™€ ν•¨κ»˜ Google νƒœκ·Έ κ΄€λ¦¬μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 뢄석을 μ‚½μž…ν•©λ‹ˆλ‹€. nextjs / reactμ—μ„œ μž‘λ™ν•˜λŠ” 데 ν•„μš”ν•œ Google νƒœκ·Έ κ΄€λ¦¬μž μ½”λ“œμ— λͺ‡ 가지 μž‘μ€ λ³€κ²½ 사항이 μžˆμŠ΅λ‹ˆλ‹€.

<Head>
      <script dangerouslySetInnerHTML={{__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
        new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
      })(window,document,'script','dataLayer','GTM-XXXXXX');`}} />
    </Head>
    <noscript dangerouslySetInnerHTML={{__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX" height="0" width="0" style="display:none;visibility:hidden;"></iframe>`}} />

image

λͺ¨λ“  74 λŒ“κΈ€

체크 아웃 <Head>

@robinvdvleuten GoogleTagManager ꡬ성 μš”μ†Œλ₯Ό λ§Œλ“€κ³  κ΅¬ν˜„ 선택에 따라 Head λ˜λŠ” λ°”λ‘œ 뒀에 ν¬ν•¨ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

μ‚¬λžŒλ“€μ΄ μ–ΈκΈ‰ν–ˆλ“―μ΄ Head νƒœκ·Έ λ˜λŠ” https://github.com/react-ga/react-ga 와 같은 λ°˜μ‘ μΉœν™”μ  인 μ†”λ£¨μ…˜μ„ μ‚¬μš©ν•  수

react-gaκ°€ next.js둜 μž‘μ—…ν•˜λŠ” 데 λŒ€ν•œ 팁이 μžˆμŠ΅λ‹ˆκΉŒ?

@gtramontina μž‘λ™ν•©λ‹ˆλ‹€. 그렇지 μ•ŠμœΌλ©΄ μ €λŠ” react-ga κΈ°μ—¬μž 쀑 ν•˜λ‚˜μ΄λ―€λ‘œ 도움을 λ“œλ¦΄ 수 μžˆμŠ΅λ‹ˆλ‹€.

react-ga ν•„μš” μ—†μŠ΅λ‹ˆλ‹€. <Head> ꡬ성 μš”μ†Œμ™€ ν•¨κ»˜ Google νƒœκ·Έ κ΄€λ¦¬μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 뢄석을 μ‚½μž…ν•©λ‹ˆλ‹€. nextjs / reactμ—μ„œ μž‘λ™ν•˜λŠ” 데 ν•„μš”ν•œ Google νƒœκ·Έ κ΄€λ¦¬μž μ½”λ“œμ— λͺ‡ 가지 μž‘μ€ λ³€κ²½ 사항이 μžˆμŠ΅λ‹ˆλ‹€.

<Head>
      <script dangerouslySetInnerHTML={{__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
        new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
      })(window,document,'script','dataLayer','GTM-XXXXXX');`}} />
    </Head>
    <noscript dangerouslySetInnerHTML={{__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX" height="0" width="0" style="display:none;visibility:hidden;"></iframe>`}} />

image

ν΄λΌμ΄μ–ΈνŠΈ μͺ½ νŽ˜μ΄μ§€λ³΄κΈ°λ₯Ό μΆ”μ ν•˜λŠ” 더 λ‚˜μ€ μ†”λ£¨μ…˜ 인 μžλ™ 좔적을 ꢌμž₯ν•©λ‹ˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ https://github.com/MoOx/phenomic/issues/384 λ₯Ό μ°Έμ‘°

@impronunciable 일뢀 인터넷 검색 후에도 μ—¬μ „νžˆ ν˜Όλž€ 슀럽기 λ•Œλ¬Έμ— 예제λ₯Ό 가지고 있으면 쒋을 κ²ƒμž…λ‹ˆλ‹€

  • @MoOx λŠ” react-gaκ°€ ν•„μš”ν•˜μ§€ μ•Šλ‹€λŠ” 것을 μ•”μ‹œν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. λŒ€μ‹  Google AutoTrack을 μ‚¬μš©ν•΄μ•Όν•©λ‹ˆλ‹€.
  • κ·ΈλŸ¬λ‚˜ Autotrack은 window κ°œμ²΄μ— μ•‘μ„ΈμŠ€ν•˜λŠ” μ½”λ“œλ‘œ 슀크립트 νƒœκ·Έλ₯Ό μƒμ„±ν•˜λ„λ‘ μ§€μ‹œν•˜λ―€λ‘œ μ„œλ²„ μΈ‘μ—μ„œ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • μ–΄μ¨Œλ“  react-ga와 같은 λ°˜μ‘ μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ 어디에 λ„£μ–΄μ•Όν• κΉŒμš”? render ? componentWillMount ? getInitialProps ? react-routerλ₯Ό μ‚¬μš©ν•˜κ³  λͺ¨λ“  νŽ˜μ΄μ§€μ— λŒ€ν•΄ ν•œ λ²ˆμ— μ½”λ“œλ₯Όλ‘œλ“œν•œλ‹€κ³  κ°€μ •ν•˜κΈ° λ•Œλ¬Έμ— λͺ…ν™•ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

νŽ˜μ΄μ§€ 좔적은 νŽ˜μ΄μ§€κ°€ μ„œλ²„μ—μ„œ λ Œλ”λ§ 될 λ•Œμ™€ λ‹€λ₯Έ νŽ˜μ΄μ§€λ‘œ 이동할 λ•Œ λͺ¨λ‘ μž‘λ™ν•΄μ•Όν•©λ‹ˆλ‹€. ν—€λ“œ, νŽ˜μ΄μ§€ λ ˆμ΄μ•„μ›ƒ HOC λ“±μ΄μžˆλŠ” μ „ν˜•μ μΈ Next μ•±μ—μ„œ 전체가 μ–΄λ–»κ²Œ ν†΅ν•©λ˜μ–΄μ•Όν•˜λŠ”μ§€ λ³΄λŠ” 것은 쒋을 κ²ƒμž…λ‹ˆλ‹€.

Next.js + Google Analytics Autotrack보닀 더 μ’‹κ³  / κ°„λ‹¨ν•˜κ³  / 덜 번거둭고 / μ΅œμ²¨λ‹¨ 쑰합이 μžˆλ‹€κ³  μƒκ°ν•˜λŠ”μ§€ μ•Œμ•„λ‘λ©΄ 쒋을 κ²ƒμž…λ‹ˆλ‹€.

λ‚˜λŠ” 이것을 쑰사 ν•  μˆ˜μžˆλ‹€

2017 λ…„ 1 μ›” 17 일 ν™”μš”μΌ μ˜€μ „ 7:59 SΓ©bastien Dubois [email protected]
썼닀 :

@impronunciable https://github.com/impronunciable 그것은 쒋을 κ²ƒμž…λ‹ˆλ‹€
인터넷 검색을 ν•œ 후에도 μ—¬μ „νžˆ ν˜Όλž€ μŠ€λŸ½μŠ΅λ‹ˆλ‹€ .πŸ˜ƒμ΄
λ‹€μ‹œ μ—΄λ¦¬λ‚˜μš”?

  • @MoOx https://github.com/MoOx λŠ” react-gaκ°€ 그렇지 μ•Šλ‹€λŠ” 것을 μ•”μ‹œν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.
    Google AutoTrack을 μ‚¬μš©ν•΄μ•Όν•©λ‹ˆλ‹€.
    λŒ€μ‹  https://github.com/googleanalytics/autotrack
  • ν•˜μ§€λ§Œ Autotrack은 λ‹€μŒκ³Ό 같은 μ½”λ“œλ‘œ 슀크립트 νƒœκ·Έλ₯Ό μƒμ„±ν•˜λ„λ‘ μ§€μ‹œν•©λ‹ˆλ‹€.
    μ°½ κ°œμ²΄μ— μ•‘μ„ΈμŠ€ν•˜λ―€λ‘œ μ„œλ²„ μΈ‘μ—μ„œ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • μ–΄μ¨Œλ“  react-ga와 같은 λ°˜μ‘ ꡬ성 μš”μ†Œλ₯Ό μ‚¬μš©ν•˜λŠ” 경우 μ–΄λ””μ—μ„œ
    λ„£μ–΄? λ Œλ”λ§μ—μ„œ? componentWillMount? getInitialProps? μ•„λ‹™λ‹ˆλ‹€
    React-routerλ₯Ό μ‚¬μš©ν•˜κ³ 
    λͺ¨λ“  νŽ˜μ΄μ§€μ— λŒ€ν•΄ ν•œ 번 μ½”λ”©ν•˜μ‹­μ‹œμ˜€.

νŽ˜μ΄μ§€ 좔적은 νŽ˜μ΄μ§€κ°€ μ„œλ²„μ—μ„œ λ Œλ”λ§ 될 λ•Œμ™€
그런 λ‹€μŒ λ‹€λ₯Έ νŽ˜μ΄μ§€λ‘œ 이동할 λ•Œ. 방법을 λ³΄λŠ” 것이 쒋을 κ²ƒμž…λ‹ˆλ‹€.
λͺ¨λ“  것이 ν—€λ“œ, νŽ˜μ΄μ§€κ°€μžˆλŠ” 일반적인 Next 앱에 ν†΅ν•©λ˜μ–΄μ•Όν•©λ‹ˆλ‹€.
λ ˆμ΄μ•„μ›ƒ HOC λ“±

더 λ‚˜μ€ / λ‹¨μˆœν•œ / 적은 것이 μžˆλ‹€κ³  μƒκ°ν•˜λŠ”μ§€ μ•Œλ©΄ 쒋을 κ²ƒμž…λ‹ˆλ‹€.
Next.js + Google보닀 번거둭고 μ΅œμ‹ μ˜ μ‘°ν•©
Analytics μžλ™ 좔적 ...

β€”
당신이 μ–ΈκΈ‰ λ˜μ—ˆκΈ° λ•Œλ¬Έμ— 이것을 λ°›κ³  μžˆμŠ΅λ‹ˆλ‹€.
이 이메일에 직접 λ‹΅μž₯ν•˜κ³  GitHubμ—μ„œ ν™•μΈν•˜μ„Έμš”.
https://github.com/zeit/next.js/issues/160#issuecomment-273108099 λ˜λŠ” μŒμ†Œκ±°
μ‹€
https://github.com/notifications/unsubscribe-auth/AAKHp9-2bfLXj2hMGlNlkqUSRqEL7R2Cks5rTJ8kgaJpZM4KkXxa
.

μžλ™ 좔적이 μΆ©λΆ„ν•˜λ©° ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—λ§Œ μ£Όμž…λ˜λ―€λ‘œ μ„œλ²„ 츑에 λŒ€ν•΄ 생각할 ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

예, μš°λ¦¬λŠ” μ„œλ²„ λ Œλ”λ§μ„ μΆ”μ ν•˜κ³  싢지 μ•ŠμŠ΅λ‹ˆλ‹€. μœ„μΉ˜μ™€ 같은 것듀을 λ‚œλ…ν•˜κ²Œ λ§Œλ“€ 것이기 β€‹β€‹λ•Œλ¬Έμž…λ‹ˆλ‹€.

μ•„λ‹ˆμš”. ν΄λΌμ΄μ–ΈνŠΈκ°€ μ²˜λ¦¬ν•˜λ―€λ‘œ νŽ˜μ΄μ§€κ°€ ν΄λΌμ΄μ–ΈνŠΈμ— μ˜ν•΄ μš”μ²­λ˜κ³  ν΄λΌμ΄μ–ΈνŠΈ λΈŒλΌμš°μ €μ—μ„œ λ Œλ”λ§λœλ‹€λŠ” 점에 μœ μ˜ν•˜μ„Έμš”. :)

OK λ‚˜λŠ” μ½”λ“œκ°€ μ„œλ²„ μΈ‘μ—μ„œ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ” 것을 깨닫지 λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ @MoOx μ—μ„œ ꢌμž₯ν•˜λŠ” μžλ™ 좔적을 μ‚¬μš© https://github.com/relatenow/relate/commit/50dc3f317eb3efa045d5bbe40d1a1a1ad1116458

νŽ˜μ΄μ§€ 쑰회수λ₯Ό μ˜¬λ°”λ₯΄κ²Œ κ³„μ‚°ν•˜λŠ”μ§€ 아직 ν™•μΈν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 이것은 λ‚˜μ—κ²Œ μƒˆλ‘œμš΄ κ²ƒμž…λ‹ˆλ‹€ 😬 πŸ˜„

"μ‹€μ‹œκ°„"보기λ₯Ό μ‚¬μš©ν•˜μ—¬ GAλ₯Ό μ‰½κ²Œ 디버깅 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

@MoOx 예, 그것이 μ €λ₯Ό κ΄΄λ‘­νžˆλŠ” κ²ƒμž…λ‹ˆλ‹€. λ‹€λ₯Έ νƒ­μ—μ„œ https://relate.now.sh λ₯Ό λ³‘λ ¬λ‘œ λ°©λ¬Έ ν•  λ•Œ μ‹€μ‹œκ°„ νŒ¨λ„μ— 아무것도 ν‘œμ‹œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ (ν™œμ„± μ‚¬μš©μž 0 λͺ…).

λ‚˜λŠ” 아직도 이것을 얻지 λͺ»ν•œλ‹€. λ‚˜λŠ” λ‚΄κ°€ μž‘μ„±ν•œ μ½”λ“œλ‘œ μ–΄λ–€ 데이터도 얻지 λͺ»ν•œλ‹€.

λ‚΄κ°€ λ³΄λŠ” λͺ¨λ“  μ˜ˆμ œμ—λŠ” <script href="https://www.google-analytics.com/analytics.js" async /> κ°€ ν•„μš”ν•œ 것 κ°™μ§€λ§Œ ν—€λ“œμ˜ 일이 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. (λ‚΄ console.logλŠ” λΈŒλΌμš°μ € μ½˜μ†”μ— μΈμ‡„λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ ...)

μ˜ˆλŠ” μ—¬μ „νžˆ 맀우 κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€ πŸ™‚

μ•„, react-ga둜 μ „ν™˜ν•˜μ—¬ μž‘λ™ν–ˆμŠ΅λ‹ˆλ‹€ πŸ˜„

https://github.com/relatenow/relate/commit/a9d2bce46e5314075af7c12bbaa0266865ff25da

screen shot 2017-01-18 at 14 01 28

μ–΄λ–»κ²Œ μž‘λ™ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆκΉŒ? componentDidMount μ—μ„œ νŽ˜μ΄μ§€ λ·° ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ €κ³ ν–ˆμŠ΅λ‹ˆλ‹€.

ν”„λ‘œλ•μ…˜ μ•± GA 확인에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.

@luandro λŠ” μ„ΈλΆ€ 사항을 κΈ°μ–΅ν•˜μ§€

λ‚˜λŠ” 그것을 μ‹œλ„ν–ˆλ‹€. λ‚˜λŠ” λ‹Ήμ‹ κ³Ό λ˜‘κ°™μ΄ν–ˆκ³ , 상단에 configureAnalytics λ₯Ό ν˜ΈμΆœν•˜κ³  componentWillMount 에 νŽ˜μ΄μ§€ λ·° λ₯Ό ν˜ΈμΆœν–ˆμ§€λ§Œ μ—¬μ „νžˆ GA Checkμ—μ„œλŠ” λΆ€μ •μ μž…λ‹ˆλ‹€.

κ·€ν•˜μ˜ μ½”λ“œμ—μ„œ μ‹€μ œλ‘œ μ–΄λ””μ—μ„œλ‚˜ μ‹€μ œλ‘œ μ—°κ²°λœ νŽ˜μ΄μ§€ κ°€ μ•„λ‹Œ 것

@luandro ga(β€˜set’, β€˜page’, window.location.pathname); ga(β€˜send’, β€˜pageview’); λ₯Ό μΆ”κ°€ν•˜μ—¬ νŽ˜μ΄μ§€ λ·° 이벀트의 URL을 λ³€κ²½ν•˜μ‹­μ‹œμ˜€.

이 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” 더 λ§Žμ€ 'next.js'λ°©λ²•μ΄μžˆμ„ 수 μžˆμ§€λ§Œ νŽ˜μ΄μ§€μ—μ„œ WRT μ‹€ν–‰ μŠ€ν¬λ¦½νŠΈλŠ” λ‹€μŒκ³Ό 같이 Script ꡬ성 μš”μ†Œλ₯Ό λ§Œλ“  경우 λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

// modules/Script.js
export default ({children}) => (
  <script dangerouslySetInnerHTML={{__html: `(${children.toString()})();` }}></script>
);

그런 λ‹€μŒ λ‚˜λ₯Ό μœ„ν•΄ μΌν•œ 이것을 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

import Document, { Head, Main, NextScript } from 'next/document'
import Script from '../modules/Script';

export default class MyDocument extends Document {

  render () {
    return (
      <html>
        <body>
          <Main />
          <Script>
            {
              () => {
                console.log('foo');
              }
            }
          </Script>
          <NextScript />
        </body>
      </html>
    )
  }
}

주의 ν•  점은 λͺ¨λ“  것을 ν•¨μˆ˜λ‘œ λ¬Άμ–΄μ•Όν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

λͺ¨λ“  νŽ˜μ΄μ§€μ—μ„œ λͺ…μ‹œ 적으둜 ga ( 'send', 'pageview')λ₯Ό ν˜ΈμΆœν•˜μ§€ μ•Šκ³ λ„ κ°œλ³„ νŽ˜μ΄μ§€ λ·°λ₯Ό 좔적 ν•  수 μžˆμŠ΅λ‹ˆκΉŒ? κΌ­ν•΄μ•Όν•œλ‹€λ©΄ gaκ°€λ‘œλ“œλ˜μ—ˆλ‹€λŠ” 확신을 가지고 μ–΄λ””μ—μ„œ ν˜ΈμΆœν• κΉŒμš”?

νŽ˜μ΄μ§€ λ·°λ₯Ό κΈ°λ‘ν•˜λŠ” λ°©μ‹μœΌλ‘œ 경둜 λ³€κ²½ μ΄λ²€νŠΈμ— μ—°κ²°ν•  수 μžˆμŠ΅λ‹ˆκΉŒ?

@karthikiyengar μœ„μ˜ λ‹€λ₯Έ μ‚¬λžŒλ“€μ΄ μ œμ•ˆν•œλŒ€λ‘œ react-ga 을 μ‚¬μš©ν•˜κ³  있으며 λͺ¨λ“  νŽ˜μ΄μ§€μ˜ componentDidMount 에 νŽ˜μ΄μ§€ λ·°λ₯Ό κΈ°λ‘ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

import ReactGA from 'react-ga'

export const initGA = () => {
  console.log('GA init')
  ReactGA.initialize('UA-xxxxxxxx-x')
}
export const logPageView = () => {
  ReactGA.set({ page: window.location.pathname })
  ReactGA.pageview(window.location.pathname)
}
componentDidMount () {
    initGA()
    logPageView()
  }

@vinaypuppal 은 λͺ¨λ“  νŽ˜μ΄μ§€ ꡬ성 μš”μ†Œμ— λŒ€ν•΄ initGAλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μΌλ°˜μ μž…λ‹ˆκΉŒ?

@rongierlach ReactGA λ‚΄μ˜ componentDidMount() ReactGA μ—μ„œ componentDidMount() initialize 및 pageview λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” Layout ꡬ성 μš”μ†Œλ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

export default class extends Component {
  componentDidMount() {
    ReactGA.initialize('UA-1234567-1')
    ReactGA.pageview(document.location.pathname)
  }

  render() {
    return (
      <div>
        {this.props.children}
     </div>
    )
}

이것은 νŽ˜μ΄μ§€κ°€ μ„œλ²„ λ Œλ”λ§ 된 κ²ƒμ²˜λŸΌ μ΄ˆκΈ°ν™”ν•΄μ•Όν•˜λŠ” κ²ƒμ²˜λŸΌ λ‚΄κ°€ ν•œ κ°€μž₯ κΉ¨λ—ν•œ λ°©λ²•μž…λ‹ˆλ‹€. ν΄λΌμ΄μ–ΈνŠΈκ°€ λ Œλ”λ§λ˜λ©΄ μ΄ˆκΈ°ν™”λ₯Ό 우회 ν•  μˆ˜μžˆλŠ” 후크가 μžˆλ‹€κ³  ν™•μ‹ ν•©λ‹ˆλ‹€.

@notrab 잠재적으둜 얕은 λ Œλ”λ§μ„

componentDidMount() {
    ReactGA.initialize('xx-xxxxxxx-x')
    let trackMe = true

    if (trackMe) {
      ReactGA.pageview(document.location.pathname)
      trackMe = false
    }

    Router.onRouteChangeStart = () => {
      NProgress.start()
      this.store.dispatch(transitionPage(true))
      trackMe = true
    }
    Router.onRouteChangeComplete = () => {
      NProgress.done()
      this.store.dispatch(transitionPage(false))
      if (trackMe) { ReactGA.pageview(document.location.pathname) }
    }
    Router.onRouteChangeError = () => {
      NProgress.done()
      this.store.dispatch(transitionPage(false))
    }

    this.store.dispatch(calculateResponsiveState(window))
  }

이 μž‘μ—…μ„ μ‹œλ„ν–ˆμ§€λ§Œ μ—¬μ „νžˆ Google Analyticsμ—μ„œ 응닡이 μ—†μŠ΅λ‹ˆλ‹€. 이것은 react-ga.js λ‚΄ μ½”λ“œμž…λ‹ˆλ‹€.

import ReactGA from 'react-ga'

const dev = process.env.NODE_ENV !== 'production'

export const initGA = () => {
  ReactGA.initialize("UA-XXXXXXXXX-X", {
    debug: dev,
  })
}

export const trackPageView = (
  pageName = window.location.pathname + window.location.search
) => {
  ReactGA.set({page: pageName})
  ReactGA.pageview(pageName)
}

export const trackCustomEvent = (category, action) =>
  ReactGA.event({category, action})

export default undefined

그리고 λ‚΄ 색인 νŽ˜μ΄μ§€ :

import {initGA, trackPageView} from '../modules/react-ga.js'
[...]
Index.componentDidMount = function() {
    initGA()
    trackPageView()
}

λ¬Όλ‘  UA μ½”λ“œλ₯Ό μ‹€μ œ μ½”λ“œλ‘œ λŒ€μ²΄ν–ˆμŠ΅λ‹ˆλ‹€.

@filostrato μ–΄λ–»κ²Œ

@exogenesys κ²°κ΅­ utils/analytics.js λ„£μ—ˆμŠ΅λ‹ˆλ‹€.

import ReactGA from 'react-ga'

const dev = process.env.NODE_ENV !== 'production'

export const initGA = () => {
  ReactGA.initialize("UA-xxxxxxxxx-x", {
    debug: dev,
  })
}

export const logPageView = (
  pageName = window.location.pathname + window.location.search
) => {
  ReactGA.set({page: pageName})
  ReactGA.pageview(pageName)
}

export const trackCustomEvent = (category, action) =>
  ReactGA.event({category, action})

export default undefined

Layout.js λ₯Ό μ—°μž₯ν•˜λ„λ‘ React.Component :

export default class Layout extends React.Component {
    componentDidMount () {
      if (!window.GA_INITIALIZED) {
        initGA()
        window.GA_INITIALIZED = true
    }
    logPageView()
  }

  render () {
    return (
      <Main>
        <Header />
        {this.props.children}
      </Main>
    )
  }
}

getInitialProps λ₯Ό μ‚¬μš©ν•˜λŠ” ꡬ성 μš”μ†Œμ—μ„œ μž‘λ™ν•˜λ„λ‘ν•˜λŠ” 방법을 찾지 λͺ»ν–ˆμ§€λ§Œ Layout.js λŠ” μ œλŒ€λ‘œ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 더 μžμ„Έν•œ 톡계λ₯Ό μ›ν•˜λ©΄ μ–΄λ–»κ²Œν•΄μ•Όν• μ§€ λͺ¨λ₯΄κ² μ§€λ§Œ κ·Έ 닀리λ₯Ό 건널 λ•Œ κ±΄λ„ˆκ² μŠ΅λ‹ˆλ‹€.

@filostrato 그게 ν•΄λƒˆμ–΄. κ°μ‚¬ν•©λ‹ˆλ‹€. :)

μ–˜λ“€ μ•„

μ €λŠ” 제 μ‚¬μ΄νŠΈμ—μ„œλ„ GA κ΅¬ν˜„μ„ λ‹€ 뀘고 이것이 μ œκ°€ 생각 ν•΄λ‚Έ κ²ƒμž…λ‹ˆλ‹€.

λ¨Όμ € κΉ¨λ—ν•˜κ³  직관적 인 @notrab 의 μ†”λ£¨μ…˜μ„ μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

export default class extends Component {
  componentDidMount() {
    ReactGA.initialize('UA-XXXXXXX-X')
    ReactGA.pageview(document.location.pathname)
  }

  render() {
    …
  }
}

κ·ΈλŸ¬λ‚˜ λ‚˜λŠ” λ‹€μŒμ„ λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

  • λ‹€λ₯Έ μœ ν˜•μ˜ νŽ˜μ΄μ§€κ°„μ— μ „ν™˜ ν•  λ•Œ componentDidMount κ°€ μ‹€ν–‰λ˜μ—ˆμœΌλ―€λ‘œ initialize ν•¨μˆ˜κ°€ μ—¬λŸ¬ 번 ν˜ΈμΆœλ˜μ—ˆμŠ΅λ‹ˆλ‹€.
  • componentDidMount λŠ” λ‹€λ₯Έ μœ ν˜•μ˜ νŽ˜μ΄μ§€κ°„μ— μ „ν™˜ ν•  λ•Œ _only_ μ‹€ν–‰λ˜μ—ˆμœΌλ―€λ‘œ pageview ν•¨μˆ˜κ°€ 호좜 된 μ΄ν›„μ—λ§Œ => μ›Ή μ‚¬μ΄νŠΈμ— video 이 μžˆλŠ”λ° λ‹€λ₯Έ λΉ„λ””μ˜€ νŽ˜μ΄μ§€ 사이λ₯Ό μ „ν™˜ν•˜λŠ” λ™μ•ˆ 초기 λΉ„λ””μ˜€ νŽ˜μ΄μ§€ 만 좔적 된 GA 라이브 λ·°

이 두 가지 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ κ³ μ°¨ ꡬ성 μš”μ†Œλ₯Ό λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. μ²˜μŒμ—λŠ” @filostrato μ†”λ£¨μ…˜μ„ μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

import React, { Component } from 'react';
import ReactGA from 'react-ga';
import Router from 'next/router';

const debug = process.env.NODE_ENV !== 'production';

export default (WrappedComponent) => (
  class GaWrapper extends Component {
    constructor (props) {
      super(props);
      this.trackPageview = this.trackPageview.bind(this);
    }

    componentDidMount() {
      this.initGa();
      this.trackPageview();
      Router.router.events.on('routeChangeComplete', this.trackPageview);
    }

    componentWillUnmount() {
      Router.router.events.off('routeChangeComplete', this.trackPageview);
    }

    trackPageview (path = document.location.pathname) {
      if (path !== this.lastTrackedPath) {
        ReactGA.pageview(path);
        this.lastTrackedPath = path;
      }
    }

    initGa () {
      if (!window.GA_INITIALIZED) {
        ReactGA.initialize('UA-XXXXXX-XX', { debug });
        window.GA_INITIALIZED = true;
      }
    }

    render() {
      return (
        <WrappedComponent {...this.props} />
      );
    }
  }
);

그리고 <Layout> / <PageWrapper> / <PageScaffold> ꡬ성 μš”μ†Œ λ˜λŠ” 이름을 μ§€μ •ν•©λ‹ˆλ‹€.

import GaWrapper from './ga-wrapper';

const PageScaffold = () => (…);

export default GaWrapper(PageScaffold);

μ•„λ§ˆλ„ 그것은 λˆ„κ΅¬μ—κ²Œλ‚˜ 도움이 될 κ²ƒμž…λ‹ˆλ‹€.

ν•˜μ§€λ§Œ Router.router.events.… ν˜ΈμΆœμ€ λ¬Έμ„œν™” 된 API의 일뢀가 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— λ§ˆμŒμ— 듀지 μ•ŠμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ˜¬λ°”λ₯Έ 방법이어야 ν•˜λŠ” Router.onRouteChangeComplete λ₯Ό ν˜ΈμΆœν•˜λ €κ³ ν•˜λ©΄ onRouteChangeComplete 이 undefined μ˜€κΈ° λ•Œλ¬Έμ— ReferenceErrorκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ¬Έμ„œκ°€ κ΅¬μ‹μΌκΉŒμš”?

이것은 next.js μ›Ή μ‚¬μ΄νŠΈμ—μ„œ μ €μ—κ²Œ νš¨κ³Όμ μ΄μ—ˆμŠ΅λ‹ˆλ‹€. 그리고 react-ga의 일반적인 κ΅¬ν˜„κ³Ό ν•¨κ»˜ μ‹€ν–‰ν•˜λ˜ 'window not defined'였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

@osartun μ†”λ£¨μ…˜μ΄ μ €μ—κ²Œ

@osartun 의 μ½”λ“œ 덕뢄에 Google의 μƒˆλ‘œμš΄ gtag (_ universal analytics_ 슀크립트λ₯Ό λŒ€μ²΄ 함)λ₯Ό μ‚¬μš©ν•˜μ—¬ pageview 이벀트λ₯Ό μ‹€ν–‰ν•˜λŠ” λ‹€μŒ μ†”λ£¨μ…˜μ„ 찾을 μˆ˜μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

pages/_document.js

import Document, { Head } from 'next/document';

const GA_TRACKING_ID = '..';

export default class MyDocument extends Document {
  render() {
    return (
      <html lang="en-AU">
        <Head>
          <script async src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`} />
          <script
            dangerouslySetInnerHTML={{
              __html: `
                window.dataLayer = window.dataLayer || [];
                function gtag(){dataLayer.push(arguments)};
                gtag('js', new Date());
                gtag('config', '${GA_TRACKING_ID}');
              `,
            }}
          />
        </Head>
        ...
      </html>
    );
  }
}

scaffold.js / layout.js / wraper.js / ν”„λ‘œμ νŠΈ 이름 :

import url from 'url';
import Router from 'next/router';

const GA_TRACKING_ID = '...';

const withPageViews = WrappedComponent =>
  class GaWrapper extends React.Component {
    componentDidMount() {
      // We want to do this code _once_ after the component has successfully
      // mounted in the browser only, so we use a special semiphore here.
      if (window.__NEXT_ROUTER_PAGEVIEW_REGISTERED__) {
        return;
      }

      window.__NEXT_ROUTER_PAGEVIEW_REGISTERED__ = true;
      let lastTrackedUrl = '';

      // NOTE: No corresponding `off` as we want this event listener to exist
      // for the entire lifecycle of the page
      // NOTE: This does _not_ fire on first page load. This is what we want
      // since GA already tracks a page view when the tag is first loaded.
      Router.router.events.on('routeChangeComplete', (newUrl = document.location) => {
        if (newUrl === lastTrackedUrl || !window.gtag) {
          return;
        }

        // Don't double track the same URL
        lastTrackedUrl = newUrl;

        // Believe it or not, this triggers a new pageview event!
        // https://developers.google.com/analytics/devguides/collection/gtagjs/single-page-applications
        window.gtag('config', GA_TRACKING_ID, {
          page_path: url.parse(newUrl).path,
        });
      });
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };

const PageScaffold = () => (…);

export default withPageViews(PageScaffold);

νŒŒν‹°μ— 늦게 μ˜€λŠ” μ‚¬λžŒμ—κ²Œ @kylewiedman 의 μ†”λ£¨μ…˜μ΄ μž‘λ™ν•˜μ§€ μ•ŠλŠ” μ΄μœ λŠ” λΌμš°ν„° μ΄λ²€νŠΈκ°€ componentDidMount 내뢀에 μ •μ˜λ˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. ꡬ성 μš”μ†Œ μ™ΈλΆ€μ—μ„œ μ •μ˜ν•˜λ©΄ λ¬Έμ œκ°€ ν•΄κ²°λ˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.

https://gist.github.com/trezy/e26cb7feb2349f585d2daf449411d0a4

@trezyλ₯Ό μ‚¬μš©ν•  λ•Œ gtag , import ReactGA from 'react-ga' ν•„μš”κ°€μ—†λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.

<script dangerouslySetInnerHtml={...} /> 예제λ₯Ό μ‚¬μš©ν•˜κ³  λ‹€μŒ ν•œ 쀄을 μ£Όμž…μ— μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

window.gaTrackingId = '${gaTrackingId}';

κ·Έ 후이 κ°€λ²Όμš΄ 트릭이 μž‘λ™ν•˜κΈ° μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€.

Router.onRouteChangeComplete = () => {
  if (window.gtag) {
    window.gtag('config', window.gaTrackingId, {
      page_location: window.location.href,
      page_path: window.location.pathname,
      page_title: window.document.title,
    });
  }
};

κ½€ 큰 analytics.js λŠ” 더 이상 κ°€μ Έ μ˜€μ§€ μ•Šμ§€λ§Œ λͺ¨λ“  νŽ˜μ΄μ§€λŠ” μ—¬μ „νžˆ μΆ”μ λ©λ‹ˆλ‹€!

이것은 λΆ„λͺ…ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 리포지토리에 μ˜ˆμ œκ°€ 있으면 쒋을 κ²ƒμž…λ‹ˆλ‹€.

λ˜λŠ” μ°¨μ„ΈλŒ€ Google 뢄석 μ €μž₯μ†Œ πŸ•΅οΈ

@prichodko 이 예제λ₯Ό

κ½€ 쒋은 libλ₯Ό μ°Ύμ•˜κ³  componentDidMount ()의 κΈ°λ³Έ λ ˆμ΄μ•„μ›ƒ νŒŒμΌμ— μΆ”κ°€λ˜μ—ˆμœΌλ©° μ˜¬λ°”λ₯΄κ²Œ μž‘λ™ν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. λ¬Έμ„œν™”κ°€ μž˜λ˜μ–΄μžˆμ–΄ λ²ˆκ±°λ‘­μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

https://www.npmjs.com/package/react-gtm-module

@andylacko 사싀, 예λ₯Ό λ“€μ–΄ HTTPS와 같은 λͺ‡ 가지 사항을 μˆ˜μ •ν•˜κΈ° μœ„ν•΄ λΆ„κΈ°ν–ˆμŠ΅λ‹ˆλ‹€. https://github.com/Vadorequest/react-gtm

λ‚˜μ€‘μ— λˆ„κ΅°κ°€μ—κ²Œ 유용 ν•  경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ 여기에 λ‚΄ μ†”λ£¨μ…˜μ„ μΆ”κ°€ν•˜μ‹­μ‹œμ˜€.

react-gaλ₯Ό μ‚¬μš© ν–ˆμ§€λ§Œ λ™μΌν•œ 둜직이 λ‹€λ₯Έ ga λ„κ΅¬μ—μ„œλ„ μž‘λ™ν•©λ‹ˆλ‹€.

_app.js 의 getInitialPropsλŠ” μ‚¬μš©μžκ°€ μƒˆ 경둜λ₯Ό 클릭 ν•  λ•Œλ§ˆλ‹€ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€. (μ²˜μŒμ—λŠ” μ„œλ²„ μΈ‘μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€).

componentDidMount 쀑 _app.js λŠ” ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œλ§Œ μ‹€ν–‰λ©λ‹ˆλ‹€.

κ·Έλž˜μ„œ _app.js 에 λ‹€μŒ λͺ‡ μ€„μ˜ μ½”λ“œλ₯Ό μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);      
    }

    // client-side only, run on page changes, do not run on server (SSR)
    if (typeof(window) === "object") {
      ReactGA.pageview(ctx.asPath);
    }
    return { pageProps, router }
  }


  componentDidMount() {
    // client-side only, run once on mount
    ReactGA.initialize('UA-XXXXXXX-3');
    ReactGA.pageview(window.location.pathname + window.location.search);
  }

νŽ˜μ΄μ§€κ°€ μ„œλ²„μ—μ„œ λ Œλ”λ§ 될 λ•Œ typeof(window) === "object" GA μ½”λ“œλ₯Ό λž˜ν•‘ν–ˆκΈ° λ•Œλ¬Έμ— getInitialProps μ—μ„œλŠ” 아무 일도 μΌμ–΄λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λŠ” λͺ¨λ“  것이 μ„œλ²„ λ Œλ”λ§μ΄κΈ° λ•Œλ¬Έμ— 처음으둜 μ‹€ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. GAλŠ” ν΄λΌμ΄μ–ΈνŠΈ 츑의 componentDidMount 내에 μ„€μ •λ©λ‹ˆλ‹€.

후속 경둜 λ³€κ²½ (λ™μΌν•œ κ²½λ‘œμ— λŒ€ν•΄μ„œλ„)은 ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λ₯Ό νŠΈλ¦¬κ±°ν•˜κ³  ga μ΄λ²€νŠΈκ°€ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€.

λ‚˜μ€‘μ— λˆ„κ΅°κ°€μ—κ²Œ 유용 ν•  경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ 여기에 λ‚΄ μ†”λ£¨μ…˜μ„ μΆ”κ°€ν•˜μ‹­μ‹œμ˜€.

react-gaλ₯Ό μ‚¬μš© ν–ˆμ§€λ§Œ λ™μΌν•œ 둜직이 λ‹€λ₯Έ ga λ„κ΅¬μ—μ„œλ„ μž‘λ™ν•©λ‹ˆλ‹€.

_app.js 의 getInitialPropsλŠ” μ‚¬μš©μžκ°€ μƒˆ 경둜λ₯Ό 클릭 ν•  λ•Œλ§ˆλ‹€ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€. (μ²˜μŒμ—λŠ” μ„œλ²„ μΈ‘μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€).

componentDidMount 쀑 _app.js λŠ” ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œλ§Œ μ‹€ν–‰λ©λ‹ˆλ‹€.

κ·Έλž˜μ„œ _app.js 에 λ‹€μŒ λͺ‡ μ€„μ˜ μ½”λ“œλ₯Ό μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);      
    }

    // client-side only, run on page changes, do not run on server (SSR)
    if (typeof(window) === "object") {
      ReactGA.pageview(ctx.asPath);
    }
    return { pageProps, router }
  }


  componentDidMount() {
    // client-side only, run once on mount
    ReactGA.initialize('UA-XXXXXXX-3');
    ReactGA.pageview(window.location.pathname + window.location.search);
  }

νŽ˜μ΄μ§€κ°€ μ„œλ²„μ—μ„œ λ Œλ”λ§ 될 λ•Œ typeof(window) === "object" GA μ½”λ“œλ₯Ό λž˜ν•‘ν–ˆκΈ° λ•Œλ¬Έμ— getInitialProps μ—μ„œλŠ” 아무 일도 μΌμ–΄λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λŠ” λͺ¨λ“  것이 μ„œλ²„ λ Œλ”λ§μ΄κΈ° λ•Œλ¬Έμ— 처음으둜 μ‹€ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. GAλŠ” ν΄λΌμ΄μ–ΈνŠΈ 츑의 componentDidMount 내에 μ„€μ •λ©λ‹ˆλ‹€.

후속 경둜 λ³€κ²½ (λ™μΌν•œ κ²½λ‘œμ— λŒ€ν•΄μ„œλ„)은 ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λ₯Ό νŠΈλ¦¬κ±°ν•˜κ³  ga μ΄λ²€νŠΈκ°€ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€.

μ•ˆλ…• 윌. μ›ΉνŒ©μ— ReactGAλ₯Ό ν¬ν•¨ν•˜λ©΄ (ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ μ‹€ν–‰λ˜λ―€λ‘œ) λΉŒλ“œ 크기에 μƒλ‹Ήν•œ 영ν–₯을 λ―ΈμΉ©λ‹ˆ 까?

@ChrisEdson https://github.com/react-ga/react-ga 전체 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 161kb μ••μΆ•λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

흠. μ†”μ§νžˆ λ§ν•΄μ„œ 정말 μ—„μ²­λ‚©λ‹ˆλ‹€!

2019 λ…„ 2 μ›” 28 일 λͺ©μš”일 19:01에 William Li [email protected] 은 λ‹€μŒκ³Ό 같이 μΌμŠ΅λ‹ˆλ‹€.

@ChrisEdson https://github.com/ChrisEdson
https://github.com/react-ga/react-ga 전체 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 161kb μ••μΆ•λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

β€”
당신이 μ–ΈκΈ‰ λ˜μ—ˆκΈ° λ•Œλ¬Έμ— 이것을 λ°›κ³  μžˆμŠ΅λ‹ˆλ‹€.
이 이메일에 직접 λ‹΅μž₯ν•˜κ³  GitHubμ—μ„œ ν™•μΈν•˜μ„Έμš”.
https://github.com/zeit/next.js/issues/160#issuecomment-468395136 λ˜λŠ” μŒμ†Œκ±°
μ‹€
https://github.com/notifications/unsubscribe-auth/AC5okwth_o_BLLPeqIcSBmhKineAhYfgks5vSCeUgaJpZM4KkXxa
.

전체 λΌμ΄λΈŒλŸ¬λ¦¬μ— λŒ€ν•΄ GitHub에 λ‚˜μ—΄λœ λ²ˆν˜Έμž…λ‹ˆλ‹€.
μ•±μ—μ„œ νŒ¨ν‚€μ§• 된 μ‹€μ œ ν¬κΈ°λŠ” μ–΄λ–»κ²Œ ν™•μΈν•˜λ‚˜μš”?

λ‚΄ iPhoneμ—μ„œ 보냄

2019 λ…„ 3 μ›” 1 일 15:43에 Chris Edson [email protected] 은 λ‹€μŒκ³Ό 같이 μΌμŠ΅λ‹ˆλ‹€.

흠. μ†”μ§νžˆ λ§ν•΄μ„œ 정말 μ—„μ²­λ‚©λ‹ˆλ‹€!

2019 λ…„ 2 μ›” 28 일 λͺ©μš”일 19:01에 William Li [email protected] 은 λ‹€μŒκ³Ό 같이 μΌμŠ΅λ‹ˆλ‹€.

@ChrisEdson https://github.com/ChrisEdson
https://github.com/react-ga/react-ga 전체 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 161kb μ••μΆ•λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

β€”
당신이 μ–ΈκΈ‰ λ˜μ—ˆκΈ° λ•Œλ¬Έμ— 이것을 λ°›κ³  μžˆμŠ΅λ‹ˆλ‹€.
이 이메일에 직접 λ‹΅μž₯ν•˜κ³  GitHubμ—μ„œ ν™•μΈν•˜μ„Έμš”.
https://github.com/zeit/next.js/issues/160#issuecomment-468395136 λ˜λŠ” μŒμ†Œκ±°
μ‹€
https://github.com/notifications/unsubscribe-auth/AC5okwth_o_BLLPeqIcSBmhKineAhYfgks5vSCeUgaJpZM4KkXxa
.

β€”
λŒ“κΈ€μ„ λ‹¬μ•˜ κΈ° λ•Œλ¬Έμ— μˆ˜μ‹  ν•œ κ²ƒμž…λ‹ˆλ‹€.
이 이메일에 직접 λ‹΅μž₯ν•˜κ±°λ‚˜ GitHubμ—μ„œ λ³΄κ±°λ‚˜ μŠ€λ ˆλ“œλ₯Ό μŒμ†Œκ±°ν•˜μ‹­μ‹œμ˜€.

λ”°λΌμ„œ μΆ•μ†Œ 된 μŠ€ν¬λ¦½νŠΈλŠ” 15kbμž…λ‹ˆλ‹€. μ•„μ£Ό μž‘μŠ΅λ‹ˆλ‹€.

λ‚΄ μ›ΉνŒ©μ„ λ²ˆλ“€λ‘œ λΆ„μ„ν•˜κ³  μ‹€μ œλ‘œ μ–Όλ§ˆλ‚˜ μΆ”κ°€λ˜λŠ”μ§€λ³΄κ³  ν•  κ²ƒμž…λ‹ˆλ‹€.

~ 500kb의 총 λ²ˆλ“€ 크기 λ‚΄μ—μ„œ ReactGAλŠ” μ•½ 27kbλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. μ›…μž₯ν•œ κ³„νšμ—μ„œ 맀우 μž‘μŠ΅λ‹ˆλ‹€.

λ‚˜μ€‘μ— λˆ„κ΅°κ°€μ—κ²Œ 유용 ν•  경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ 여기에 λ‚΄ μ†”λ£¨μ…˜μ„ μΆ”κ°€ν•˜μ‹­μ‹œμ˜€.

react-gaλ₯Ό μ‚¬μš© ν–ˆμ§€λ§Œ λ™μΌν•œ 둜직이 λ‹€λ₯Έ ga λ„κ΅¬μ—μ„œλ„ μž‘λ™ν•©λ‹ˆλ‹€.

_app.js 의 getInitialPropsλŠ” μ‚¬μš©μžκ°€ μƒˆ 경둜λ₯Ό 클릭 ν•  λ•Œλ§ˆλ‹€ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€. (μ²˜μŒμ—λŠ” μ„œλ²„ μΈ‘μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€).

componentDidMount 쀑 _app.js λŠ” ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œλ§Œ μ‹€ν–‰λ©λ‹ˆλ‹€.

κ·Έλž˜μ„œ _app.js 에 λ‹€μŒ λͺ‡ μ€„μ˜ μ½”λ“œλ₯Ό μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);      
    }

    // client-side only, run on page changes, do not run on server (SSR)
    if (typeof(window) === "object") {
      ReactGA.pageview(ctx.asPath);
    }
    return { pageProps, router }
  }


  componentDidMount() {
    // client-side only, run once on mount
    ReactGA.initialize('UA-XXXXXXX-3');
    ReactGA.pageview(window.location.pathname + window.location.search);
  }

νŽ˜μ΄μ§€κ°€ μ„œλ²„μ—μ„œ λ Œλ”λ§ 될 λ•Œ typeof(window) === "object" GA μ½”λ“œλ₯Ό λž˜ν•‘ν–ˆκΈ° λ•Œλ¬Έμ— getInitialProps μ—μ„œλŠ” 아무 일도 μΌμ–΄λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λŠ” λͺ¨λ“  것이 μ„œλ²„ λ Œλ”λ§μ΄κΈ° λ•Œλ¬Έμ— 처음으둜 μ‹€ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. GAλŠ” ν΄λΌμ΄μ–ΈνŠΈ 츑의 componentDidMount 내에 μ„€μ •λ©λ‹ˆλ‹€.

후속 경둜 λ³€κ²½ (λ™μΌν•œ κ²½λ‘œμ— λŒ€ν•΄μ„œλ„)은 ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ getInitialProps λ₯Ό νŠΈλ¦¬κ±°ν•˜κ³  ga μ΄λ²€νŠΈκ°€ νŠΈλ¦¬κ±°λ©λ‹ˆλ‹€.

NextJS 9λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 λ°œμƒν•˜λŠ” λ¬Έμ œλŠ” μžλ™ 사전 λ Œλ”λ§μ„ λΉ„ν™œμ„±ν™”ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. ν’‹ 쒋은 곳이 μžˆλ‹€λ©΄ ꢁ금 ReactGA.pageview(ctx.asPath); μ—μ„œ 제거, getInitialProps μš°λ¦¬λŠ” 제거 ν•  μˆ˜μžˆλŠ” μˆ˜λ‹¨μ„ getInitialProps : 여기에 μ„€λͺ… λœλŒ€λ‘œ 옡트 아웃에 μžλ™ 사전 λ Œλ”λ§μ„ κ°•μ œν•˜μ§€ HTTPS : //err.sh/next.js/opt-out-automatic-prerendering.

μ—…λ°μ΄νŠΈ : with-google-analytics 예제 (https://github.com/zeit/next.js/tree/canary/examples/with-google-analytics)λ₯Ό μ°Ύμ•„μ„œ Router.events.on('routeChangeComplete', url => ReactGA.pageview(url)) URL이 μ˜¬λ°”λ₯Έμ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ ν…ŒμŠ€νŠΈν•΄μ•Όν•˜μ§€λ§Œ μ˜¬λ°”λ₯Έ λ°©μ‹μœΌλ‘œ λ³΄μ΄λŠ”μ§€ ν™•μΈν•΄μ•Όν•©λ‹ˆλ‹€. getInitialProps λ©”μ„œλ“œλ₯Ό μ œκ±°ν•˜κ³ μ΄λ₯Ό 클래슀 μ„ μ–Έ μœ„μ— μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

@GodOfGrandeur 이것은 https://medium.com/@austintoddj/using -google-analytics-with-next-js-423ea2d16a98 _app.js에 μ½”λ“œλ₯Ό μΆ”κ°€ν•˜λ©΄ ν•„μš”ν•œ λͺ¨λ“  κ³³μ—μ„œ νŠΈλ¦¬κ±°λ˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€ λ‚˜λŠ” λ˜ν•œ 그것이 μ„œλ²„μ—μ„œ νŠΈλ¦¬κ±°λ˜μ§€ μ•ŠλŠ” 것을 μ’‹μ•„ν•©λ‹ˆλ‹€ (λ¬Έμ œκ°€ λ˜λ”λΌλ„ 없을 κ²ƒμž…λ‹ˆλ‹€).

제 3μžκ°€ ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. gtagλ₯Ό μ‚¬μš©ν•˜λŠ” 경우 λ‚΄κ°€ λ§Œλ“  λ‹€μŒ μ½”λ“œλ₯Ό μ‚¬μš©ν•˜μ‹­μ‹œμ˜€

<script
    async
    src="https://www.googletagmanager.com/gtag/js?id=%your code here%" >
</script>
<script dangerouslySetInnerHTML={
    { __html: `
        window.dataLayer = window.dataLayer || [];
        function gtag(){window.dataLayer.push(arguments)}
        gtag("js", new Date());
        gtag("config", "<%your code here%>");
    `}
}>
</script>

또 λ‹€λ₯Έ μ˜΅μ…˜μ€ useEffect() μž…λ‹ˆλ‹€. Layout.js νŒŒμΌμ—μ„œμ΄ μž‘μ—…μ„ μˆ˜ν–‰ν–ˆμŠ΅λ‹ˆλ‹€.

useEffect(() => {
  if (process.env.NODE_ENV === 'production') {
    window.dataLayer = window.dataLayer || []
    function gtag() {
      dataLayer.push(arguments)
    }
    gtag('js', new Date())
    gtag('config', '{GOOGLE ANALYTICS CODE}', {
      page_location: window.location.href,
      page_path: window.location.pathname,
      page_title: window.document.title,
    })
  }
})

그리고 <Head> :

<Head>
  <script async src="https://www.googletagmanager.com/gtag/js?id={GOOGLE ANALYTICS CODE}"></script>
</Head>

@reinink 두 번째 μ†Œν’ˆμœΌλ‘œ 빈 배열을 useEffect μ „λ‹¬ν•˜μ—¬ 초기 λ Œλ”λ§ ν›„ ν•œ 번만 ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λ„λ‘ ν•  수 μžˆμŒμ„ μ–ΈκΈ‰ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

useEffect(() => {...}, [])

@reinink μ†”λ£¨μ…˜μ„ ν™•μž₯ν•˜λ©΄ λ ˆμ΄μ•„μ›ƒ ꡬ성 μš”μ†Œ / ν…œν”Œλ¦Ώμ„ λž˜ν•‘ν•˜λŠ” 데 μ‚¬μš©ν•˜λŠ” μž‘μ—… HOCκ°€ μžˆμŠ΅λ‹ˆλ‹€.

import React, { useEffect } from 'react'
import Head from 'next/head'
const GoogleAnalyticsHOC = ({ children }) => {
  useEffect(() => {
    if (process.env.NODE_ENV === 'production') {
      window.dataLayer = window.dataLayer || []
      // eslint-disable-next-line
      function gtag() {
        window.dataLayer.push(arguments)
      }
      gtag('js', new Date())
      gtag('config', process.env.GOOGLE_ANALYTICS_ID, {
        page_location: window.location.href,
        page_path: window.location.pathname,
        page_title: window.document.title
      })
    }
  }, [])
  return (
    <>
      <Head>
        <script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GOOGLE_ANALYTICS_ID}`} />
      </Head>
      {children}
    </>
  )
}
export default GoogleAnalyticsHOC

κ΄‘λ²”μœ„ν•˜κ²Œ ν…ŒμŠ€νŠΈλ˜μ§€λŠ” μ•Šμ•˜μ§€λ§Œ λ‘œμ»¬μ—μ„œ μ‹€ν–‰λ˜λŠ” μ‹€μ‹œκ°„ 방문자둜 μž‘λ™ν•˜κ³  ν†΅κ³Όν•©λ‹ˆλ‹€. :)

이 κ°€μ΄λ“œλ₯Ό μ°Ύμ•˜μŠ΅λ‹ˆλ‹€-이것이 ꢌμž₯λ˜λŠ” μ ‘κ·Ό λ°©μ‹μž…λ‹ˆκΉŒ (react-ga μ‚¬μš©)?

ꢌμž₯λ˜λŠ” 방법은이 곡식 예제λ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

https://github.com/zeit/next.js/tree/canary/examples/with-google-analytics

이미 여기에 κ²Œμ‹œλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. λ•Œλ‘œλŠ” github λ¬Έμ œκ°€ stackoverflow와 λΉ„μŠ·ν•΄μ§€κΈ°λ₯Ό 바라며, 질문 직후 (λ©”μ‹œμ§€κ°€ λ„ˆλ¬΄ 많고 이λͺ¨ν‹°μ½˜μ΄ μΆ©λΆ„ν•˜μ§€ μ•Šμ„ λ•Œ 일뢀 닡변을 μˆ˜λ½ν•˜κ³  κ°•μ‘° ν‘œμ‹œν•©λ‹ˆλ‹€. ν•œ 가지 μ˜΅μ…˜μ€μ΄ κ²Œμ‹œλ¬Όμ— λŒ€ν•œ λŒ“κΈ€μ„ μ œν•œν•˜λŠ” κ²ƒμ΄λ―€λ‘œ μ‚¬λžŒλ“€μ΄ μƒˆλ‘œμš΄ 문제λ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.) λŒ€μ‹ . 그리고 μƒμ„±ν•˜λŠ” λ™μ•ˆ λΉ„μŠ·ν•œ λ¬Έμ œκ°€ 이미 λ§Œλ“€μ–΄μ‘Œκ³  ( "μœ μ‚¬ν•œ 문제"κΈ°λŠ₯ 덕뢄에) 닡이 μžˆμŠ΅λ‹ˆλ‹€ (ν•˜μ§€λ§Œ μ—¬μ „νžˆ κΈ΄ μ—­μ‚¬μ˜ ν•œκ°€μš΄λ° μ–΄λ”˜κ°€μ—μ„œ 이λͺ¨ 지에 λŒ€ν•œ 닡을 μžƒμ–΄ 버리기 λ•Œλ¬Έμ— githubμ—μ„œ 닡변을 μ°ΎλŠ” 것이 그리 νŽΈλ¦¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.)

이미 여기에 κ²Œμ‹œλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. λ•Œλ‘œλŠ” github λ¬Έμ œκ°€ stackoverflow와 λΉ„μŠ·ν•˜κ³  질문 직후에 일뢀 닡변을 μˆ˜λ½ν•˜κ³  κ°•μ‘° ν‘œμ‹œν•˜κΈ°λ₯Ό λ°”λžλ‹ˆλ‹€ (λ©”μ‹œμ§€κ°€ λ„ˆλ¬΄ 많고 이λͺ¨ν‹°μ½˜μ΄ μΆ©λΆ„ν•˜μ§€ μ•Šμ€ 경우 ....

GithubλŠ”μ΄ μ €μž₯μ†Œμ˜ ν† λ‘  νƒ­μ—μ„œμ΄λ₯Ό ν”„λ‘œν†  νƒ€μ΄ν•‘ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

Screenshot 2020-04-08 at 16 59 58

@janbaykara μ™€μš°, 정말? 우리 λͺ¨λ‘λŠ” 같은 λ°©μ‹μœΌλ‘œ μƒκ°ν•©λ‹ˆλ‹€. :) 그듀이 μ–΄λ–»κ²Œ λ“  문제λ₯Ό ν† λ‘ μœΌλ‘œ λ³€ν™˜ν•˜λŠ” 방법을 μ°Ύκ±°λ‚˜ 같은 일이되기λ₯Ό λ°”λžλ‹ˆλ‹€ (κΈ°λŠ₯이 λ¬Έμ œμ— λ³‘ν•©λ˜κ³  기쑴의 λͺ¨λ“  토둠이 λ¬Έμ œκ°€λ˜λŠ” 경우). ν•˜μ§€λ§Œ 응닡 ν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€!

@ 3lonious μ €λŠ” 일반적으둜 SPA와 잘 μž‘λ™ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 개인적으둜 Google Analyticsλ₯Ό λ²„λ ΈμŠ΅λ‹ˆλ‹€. (특히 SSR + CSR 및 μΆ”μ ν•˜κΈ° μ–΄λ €μš΄ 이쀑 μ΄λ²€νŠΈμ— λŒ€ν•œ 정말 λ‚˜μœ κ²½ν—˜)

Next.js에 κ΄€ν•΄μ„œλŠ” https://github.com/UnlyEd/next-right-now/blob/v2-mst-aptd-gcms-lcz-sty/src/components/pageLayouts/Head.tsx($# μ‚¬μš©)λ₯Ό ν™•μΈν•˜μ‹­μ‹œμ˜€ next/Head ), 이것이 앱에 슀크립트 νƒœκ·Έλ₯Ό ν¬ν•¨ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

μ£Όμ €ν•˜μ§€ 말고 NRN https://unlyed.github.io/next-right-nowλ₯Ό μ‚΄νŽ΄λ³΄μ‹­μ‹œμ˜€. 이것은 Next.jsλ₯Ό μ‚¬μš©ν•˜μ—¬ "Xλ₯Ό μˆ˜ν–‰ν•˜λŠ” 방법"에 λŒ€ν•œ 쒋은 μ˜ˆκ°€λ˜κΈ°μœ„ν•œ 것이며 μƒλ‹Ήν•œ 두톡을 λœμ–΄ 쀄 κ²ƒμž…λ‹ˆλ‹€.

λ‚΄κ°€ 그것을 μ–»λŠ” 지 ν™•μ‹€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. κ·Έ νŒŒμΌμ— 슀크립트 νƒœκ·Έκ°€ 보이지 μ•ŠμŠ΅λ‹ˆκΉŒ?

이것은 λ˜ν•œ μž‘λ™ν•˜λŠ” μ†”λ£¨μ…˜μž…λ‹ˆλ‹€. λ‚˜λŠ” 그것을 κ΄‘λ²”μœ„ν•˜κ²Œ ν…ŒμŠ€νŠΈν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ 직접 μˆ˜ν–‰ν•˜μ‹­μ‹œμ˜€.

pages/_document.js μ—μ„œ <Head> μΆ”κ°€ν•˜μ„Έμš”. λ¬Έμ„œ νŒŒμΌμ„ μž¬μ •μ˜ν•˜λŠ” 방법에 λŒ€ν•œ λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

<script async src='https://www.googletagmanager.com/gtag/js?id=YOUR_GA_TRACKING_ID'></script>
<script
    dangerouslySetInnerHTML={{
        __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments)}
            gtag('js', new Date());

            gtag('config', 'YOUR_GA_TRACKING_ID');
              `
      }}>
</script>

적어도 λ‚΄ GA 계정에 λŒ€ν•œ νŠΈλž˜ν”½μ„λ³΄κ³ ν•©λ‹ˆλ‹€.

@ 3lonious Script νƒœκ·Έμ™€ μŠ€νƒ€μΌ νƒœκ·ΈλŠ” 같은 λ°©μ‹μœΌλ‘œ μž‘λ™ν•©λ‹ˆλ‹€.
μœ„μ˜ μ†”λ£¨μ…˜μ€ κ²½ν—˜μœΌλ‘œλ„ μž‘λ™ν•©λ‹ˆλ‹€. (λ‚΄κ°€ GAλ₯Ό μž‘λ™ν•˜κ²Œ λ§Œλ“œλŠ” 방법)

λ„€ κ°μ‚¬ν•©λ‹ˆλ‹€. λ‚˜λŠ” λ‹Ήμ‹ μ—κ²Œ λ‹€μ‹œ μ—°λ½ν•˜λ €κ³  λ…Έλ ₯ν•  κ²ƒμž…λ‹ˆλ‹€

μ•Œμ•˜μ–΄ λ‚΄κ°€ 일할 μˆ˜μžˆλŠ” 것 κ°™μ•„

κ°μ‚¬ν•©λ‹ˆλ‹€ Anton. 그래 λ„ˆλ₯Όμœ„ν•œ μƒˆ κ²ƒμ΄μžˆμ–΄ haha

고객 μ„œλΉ„μŠ€μ— μ‚¬μš©ν•˜λŠ” μ±„νŒ… 앱이 μžˆμ§€λ§Œ ν•΄λ‹Ή 슀크립트 νƒœκ·Έλ„ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆκΉŒ? μ–΄λ–€ 아이디어?

이 두 번째 νƒœκ·Έλ₯Ό 본문에 μΆ”κ°€ν•˜λΌλŠ” λ©”μ‹œμ§€? κ·Έκ²ƒμœΌλ‘œ λ¬΄μ—‡μ„ν•©λ‹ˆκΉŒ?
Screen Shot 2020-06-19 at 6 48 22 AM

TBTλŠ” μ–΄λ–»μŠ΅λ‹ˆκΉŒ? ꡬ글 μ• λ„λ¦¬ν‹±μŠ€λ₯Ό μΆ”κ°€ν•˜λ©΄ 속도 μ μˆ˜κ°€ λŠλ €μ§‘λ‹ˆλ‹€

TBTλŠ” μ–΄λ–»μŠ΅λ‹ˆκΉŒ? ꡬ글 μ• λ„λ¦¬ν‹±μŠ€λ₯Ό μΆ”κ°€ν•˜λ©΄ 속도 μ μˆ˜κ°€ λŠλ €μ§‘λ‹ˆλ‹€

μ½˜ν…μΈ  μ•žμ— 슀크립트λ₯Ό λ°°μΉ˜ν•˜λ„λ‘ ꢌμž₯ν–ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. μ½˜ν…μΈ  μ•„λž˜μ— λ†“μœΌλ©΄ μ μˆ˜κ°€ λ‚΄λ € 가지 μ•ŠμŠ΅λ‹ˆλ‹€.

개인적으둜 슀크립트λ₯Ό λ¨Όμ € λ°°μΉ˜ν•˜λŠ” 것이 μ‹€μ§ˆμ μΈ 이점을 보지 λͺ»ν•©λ‹ˆλ‹€. νŽ˜μ΄μ§€λ‘œλ“œκ°€ μ–΄λ–»κ²Œν•΄μ„œ λ“  κΉ¨μ Έμ„œ HTML이 μ™„μ „νžˆλ‘œλ“œλ˜μ§€ μ•Šκ³  쀑단 된 κ²½μš°μ—λŠ” 히트둜 κ°„μ£Όν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‚˜λŠ” nextjs와 ν•¨κ»˜ μΌν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. react google place μžλ™ μ™„μ„± APIλ₯Ό μ‚¬μš©ν•˜λ €κ³ ν•©λ‹ˆλ‹€. 그리고 API의 슀크립트 νƒœκ·Έλ₯Ό μ‚½μž… ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? 슀크립트 νƒœκ·ΈλŠ” 어디에 μ‚½μž…ν•΄μ•Όν•©λ‹ˆκΉŒ ??

_app.js

```js
"react-ga"μ—μ„œ ReactGA κ°€μ Έ 였기;

componentDidMount() {
  ReactGA.initialize("XX-XXXXXXXXX-X", { debug: false });
  ReactGA.set({ page: this.props.router.pathname });
  ReactGA.pageview(this.props.router.pathname);
  this.unlisten = this.props.router.events.on("routeChangeComplete", (router) => {
      ReactGA.set({ page: router });
      ReactGA.pageview(router);
    });
}

이것은 μž¬λ°ŒμŠ΅λ‹ˆλ‹€. μ•„λ‹ˆ, μ‹€μ œλ‘œ 이것은 μŠ¬ν”„λ‹€. React, Next, 전체 μƒνƒœκ³„κ°€ μ£½κΈ°λ₯Ό 기닀릴 수 μ—†μŠ΅λ‹ˆλ‹€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰