μλ !
νμ¬ λ΄ μΉ μ¬μ΄νΈλ₯Ό next.js νλ‘μ νΈλ‘ μ ν μ€μ΄λ©° νμ΄μ§μ Google μ λ리ν±μ€ μ€ν¬λ¦½νΈ νκ·Έλ₯Ό μΆκ°νλ λ°©λ²μ μ μ μμ΅λλ€. μ΄κ²¬μλ μ¬λ?
건배,
λ¨μ μ΄λ¦
μ²΄ν¬ μμ <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>`}} />
ν΄λΌμ΄μΈνΈ μͺ½ νμ΄μ§λ³΄κΈ°λ₯Ό μΆμ νλ λ λμ μ루μ μΈ μλ μΆμ μ κΆμ₯ν©λλ€. μμΈν λ΄μ©μ https://github.com/MoOx/phenomic/issues/384 λ₯Ό μ°Έμ‘°
@impronunciable μΌλΆ μΈν°λ· κ²μ νμλ μ¬μ ν νΌλ μ€λ½κΈ° λλ¬Έμ μμ λ₯Ό κ°μ§κ³ μμΌλ©΄ μ’μ κ²μ λλ€
window
κ°μ²΄μ μ‘μΈμ€νλ μ½λλ‘ μ€ν¬λ¦½νΈ νκ·Έλ₯Ό μμ±νλλ‘ μ§μνλ―λ‘ μλ² μΈ‘μμ μλνμ§ μμ΅λλ€.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
μ΄λ»κ² μλνκ² λμμ΅λκΉ? 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 ()μ κΈ°λ³Έ λ μ΄μμ νμΌμ μΆκ°λμμΌλ©° μ¬λ°λ₯΄κ² μλνλ κ² κ°μ΅λλ€. λ¬Έμνκ° μλμ΄μμ΄ λ²κ±°λ‘μ§ μμ΅λλ€.
@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λμ΄ μ μ₯μμ ν λ‘ νμμμ΄λ₯Ό νλ‘ν νμ΄ννκ³ μμ΅λλ€.
@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
κ³ κ° μλΉμ€μ μ¬μ©νλ μ±ν μ±μ΄ μμ§λ§ ν΄λΉ μ€ν¬λ¦½νΈ νκ·Έλ μλνμ§ μμ΅λκΉ? μ΄λ€ μμ΄λμ΄?
μ΄ λ λ²μ§Έ νκ·Έλ₯Ό λ³Έλ¬Έμ μΆκ°νλΌλ λ©μμ§? κ·Έκ²μΌλ‘ 무μμν©λκΉ?
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, μ 체 μνκ³κ° μ£½κΈ°λ₯Ό κΈ°λ€λ¦΄ μ μμ΅λλ€.
κ°μ₯ μ μ©ν λκΈ
react-ga
νμ μμ΅λλ€.<Head>
κ΅¬μ± μμμ ν¨κ» Google νκ·Έ κ΄λ¦¬μλ₯Ό μ¬μ©νμ¬ λΆμμ μ½μ ν©λλ€. nextjs / reactμμ μλνλ λ° νμν Google νκ·Έ κ΄λ¦¬μ μ½λμ λͺ κ°μ§ μμ λ³κ²½ μ¬νμ΄ μμ΅λλ€.