프론트엔드 개발
next.js (react.js) 에서 채널톡 커스텀 훅으로 만들어 사용하기
butter-ring
2023. 5. 11. 22:01
회사에서 맡아 운영하는 서비스에서 채널톡을 도입하였다.
공식문서에 보면 자바스크립트로 밖에 되어있지 않아서 도입을 하려면 커스텀훅을 사용해서 도입해야했다.
구글링을 했지만 딱히 뭔가 확 오는 방법이 없어서 직접 커스텀훅을 만들기로 결정!
ts 파일을 hook 디랙토리에 생성 후 일단 공식 문서에 있는 인터페이스들을 긁어왔다.
그 후 스크립트를 function으로 작성 후 채널톡에서 제공하는 기본 함수들을 타입스크립트로 변경하여 적용하였다.
declare global {
interface Window {
ChannelIO?: IChannelIO;
ChannelIOInitialized?: boolean;
}
}
interface IChannelIO {
c?: (...args: any) => void;
q?: [methodName: string, ...args: any[]][];
(...args: any): void;
}
interface BootOption {
appearance?: string;
customLauncherSelector?: string;
hideChannelButtonOnBoot?: boolean;
hidePopup?: boolean;
language?: string;
memberHash?: string;
memberId?: string;
mobileMessengerMode?: string;
pluginKey: string;
profile?: Profile;
trackDefaultEvent?: boolean;
trackUtmSource?: boolean;
unsubscribe?: boolean;
unsubscribeEmail?: boolean;
unsubscribeTexting?: boolean;
zIndex?: number;
}
interface Callback {
(error: Error | null, user: CallbackUser | null): void;
}
interface CallbackUser {
alert: number;
avatarUrl: string;
id: string;
language: string;
memberId: string;
name?: string;
profile?: Profile | null;
tags?: string[] | null;
unsubscribeEmail: boolean;
unsubscribeTexting: boolean;
}
interface UpdateUserInfo {
language?: string;
profile?: Profile | null;
profileOnce?: Profile;
tags?: string[] | null;
unsubscribeEmail?: boolean;
unsubscribeTexting?: boolean;
}
interface Profile {
[key: string]: string | number | boolean | null;
}
interface FollowUpProfile {
name?: string | null;
mobileNumber?: string | null;
email?: string | null;
}
interface EventProperty {
[key: string]: string | number | boolean | null;
}
type Appearance = 'light' | 'dark' | 'system' | null;
export default function UseChannelService() {
const loadScript = () => {
let w = window;
if (w.ChannelIO) {
return (window.console.error || window.console.log || function () {})('ChannelIO script included twice.');
}
const ch: IChannelIO = function () {
ch.c?.(arguments);
};
ch.q = [];
ch.c = function (args) {
ch.q?.push(args);
};
w.ChannelIO = ch;
function l() {
if (w.ChannelIOInitialized) {
return;
}
w.ChannelIOInitialized = true;
let s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://cdn.channel.io/plugin/ch-plugin-web.js';
let x = document.getElementsByTagName('script')[0];
if (x.parentNode) {
x.parentNode.insertBefore(s, x);
}
}
if (document.readyState === 'complete') {
l();
} else {
w.addEventListener('DOMContentLoaded', l);
w.addEventListener('load', l);
}
};
loadScript();
return () => {
window.ChannelIO?.('clearCallbacks');
};
}
export const ChannelTalk = {
boot: (option: BootOption, callback?: Callback) => {
window.ChannelIO?.('boot', option, callback);
},
shutdown: () => {
window.ChannelIO?.('shutdown');
},
showMessenger: () => {
window.ChannelIO?.('showMessenger');
},
hideMessenger: () => {
window.ChannelIO?.('hideMessenger');
},
openChat: (chatId?: string | number, message?: string) => {
window.ChannelIO?.('openChat', chatId, message);
},
track: (eventName: string, eventProperty?: EventProperty) => {
window.ChannelIO?.('track', eventName, eventProperty);
},
onShowMessenger: (callback: () => void) => {
window.ChannelIO?.('onShowMessenger', callback);
},
onHideMessenger: (callback: () => void) => {
window.ChannelIO?.('onHideMessenger', callback);
},
onBadgeChanged: (callback: (alert: number) => void) => {
window.ChannelIO?.('onBadgeChanged', callback);
},
onChatCreated: (callback: () => void) => {
window.ChannelIO?.('onChatCreated', callback);
},
onFollowUpChanged: (callback: (profile: FollowUpProfile) => void) => {
window.ChannelIO?.('onFollowUpChanged', callback);
},
onUrlClicked: (callback: (url: string) => void) => {
window.ChannelIO?.('onUrlClicked', callback);
},
updateUser: (userInfo: UpdateUserInfo, callback?: Callback) => {
window.ChannelIO?.('updateUser', userInfo, callback);
},
addTags: (tags: string[], callback?: Callback) => {
window.ChannelIO?.('addTags', tags, callback);
},
removeTags: (tags: string[], callback?: Callback) => {
window.ChannelIO?.('removeTags', tags, callback);
},
setPage: (page: string) => {
window.ChannelIO?.('setPage', page);
},
resetPage: () => {
window.ChannelIO?.('resetPage');
},
showChannelButton: () => {
window.ChannelIO?.('showChannelButton');
},
hideChannelButton: () => {
window.ChannelIO?.('hideChannelButton');
},
setAppearance: (appearance: Appearance) => {
window.ChannelIO?.('setAppearance', appearance);
},
};
처음엔 nav에서 채널톡을 실행하였지만 그러다 보니 유저의 정보를 업데이트 할 때 채널톡을 두번 호출하는 에러가 있었다.
채널톡을 사용하고 서비스를 운영하는데 크리티컬한 에러는 아니였지만 맘에 들지 않았기 때문에
_app.ts 에 useEffect로 웹 어플리케이션 실행 시 딱 한번만 커스텀 훅이 실행되게 코드를 작성하였다.
const MyApp = (props: MyAppProps) => {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
useEffect(() => {
UseChannelService();
}, []);
return (
<>
<Meta />
<CacheProvider value={emotionCache}>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<GlobalStyles
styles={{
body: { backgroundColor: '#1E1E1E' },
}}
/>
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
</CacheProvider>
</>
);
};
export default appWithTranslation(MyApp);
사용자가 웹 페이지에 접속했을때 커스텀 훅을 실행시켜 채널톡에 연동한 후 각 함수가 필요한 페이지나 컴포넌트에서
호출하여 쓰니 오류가 해결~~!!!
내가 사용한 방식이 옳은 방식인진 잘 모르겠지만 혹시라도 필요한 분들이 계시면....