링크를 상대방에게 SNS로 공유하는 기능을 구현하고 싶었다.
페이스북, 카카오, 네이버 공유하기 기능을 구현하려 한다.
* 참고로 카카오톡은 개발자에서 허용한 도메인만 링크연결이 가능하며,
페이스북과 네이버는 localhost로 접근이 안돼서 (실제사이트가 아니기때문) naver링크로 연결해두었다.
페이스북 공유하기
예시로 작성한 링크는 아래와 같다.
const link = 'https://www.naver.com/';
<button
className='share-wrapper'
onClick={() => {
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${link}`,
'페이스북 공유하기',
'width=600,height=800,location=no,status=no,scrollbars=yes' //새창 뜨는 것 조절 (없어도 작동됨)
);
}}
>
<img className='facebook shareicon' src={facebook} />
</button>
네이버 공유하기
<button
className='share-wrapper'
onClick={() => {
window.open(
`https://share.naver.com/web/shareView?url=${link}&title=네이버 공유하기`
);
}}
>
<img className='naver shareicon' src={naver} />
</button>
카카오 공유하기
위에서는 link를 임의로 네이버사이트로 설정하여 이동시켰는데, 카카오는 개발자에서 허용한 도메인만 가능하여
아래와 같이 주소를 변경하였다.
const link = 'http://localhost:3000/main';
아래 코드에서 TypeScript작성시 오류가 있어 useEffect부분은 수정함 제일 아래 참고
..
//kakao
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js';
script.async = true;
document.body.appendChild(script);
return () => document.body.removeChild(script);
}, []);
return(
..
<div className='kakao-share-button'>
<button
className='share-wrapper'
onClick={() => shareKakao(link, '카카오 공유하기')}
>
<img className='kakao shareicon' src={kakao} alt={'Kakao Logo'} />
</button>
</div>
)
onClick시 작동되는 함수
참고로 kakao.Link.sendDefaul 는 구버전이고 kakao.Share.sendDefault 로 Share로 바뀐 것이 최신버전이라 이렇게 작성함
//kakao
export const shareKakao = (route, title) => {
// url이 id값에 따라 변경되기 때문에 route를 인자값으로 받아줌
if (window.Kakao) {
const kakao = window.Kakao;
if (!kakao.isInitialized()) {
kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); // 카카오에서 제공받은 javascript key를 넣어줌 -> .env파일에서 호출시킴
}
kakao.Share.sendDefault({
objectType: 'feed', // 카카오 링크 공유 여러 type들 중 feed라는 타입 -> 자세한 건 카카오에서 확인
content: {
title: title, // 인자값으로 받은 title(공유톡에서 나타남)
description: '설명', // 인자값으로 받은 title(공유톡에서 나타남)
imageUrl: '이미지 url',
link: {
mobileWebUrl: route, // 인자값으로 받은 route(uri 형태)/카카오는 개발자에서 허용한 도메인만 가능
webUrl: route,
},
},
buttons: [
{
title: '사이트로 이동',
link: {
mobileWebUrl: route,
webUrl: route,
},
},
/* {
title: 'title',
link: {
mobileWebUrl: route,
webUrl: route,
},
}, */ //2개버튼 만들기도 가능(버튼 클릭시 이동)
],
});
}
};
전체 코드
import React, { useEffect } from 'react';
import facebook from '@assets/images/ico-user_facebook.png';
import naver from '@assets/images/ico-user-naver.png';
import kakao from '@assets/images/ico-user-kakao.png';
import './snsShare.scss';
//kakao
export const shareKakao = (route, title) => {
// url이 id값에 따라 변경되기 때문에 route를 인자값으로 받아줌
if (window.Kakao) {
const kakao = window.Kakao;
if (!kakao.isInitialized()) {
kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); // 카카오에서 제공받은 javascript key를 넣어줌 -> .env파일에서 호출시킴
}
kakao.Share.sendDefault({
objectType: 'feed', // 카카오 링크 공유 여러 type들 중 feed라는 타입 -> 자세한 건 카카오에서 확인
content: {
title: title, // 인자값으로 받은 title(공유톡에서 나타남)
description: '설명', // 인자값으로 받은 title(공유톡에서 나타남)
imageUrl: '이미지 url',
link: {
mobileWebUrl: route, // 인자값으로 받은 route(uri 형태)
webUrl: route,
},
},
buttons: [
{
title: 'title',
link: {
mobileWebUrl: route,
webUrl: route,
},
},
],
});
}
};
export default function SnsShare() {
const link = 'https://www.naver.com/';
//kakao
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js';
script.async = true;
document.body.appendChild(script);
return () => document.body.removeChild(script);
}, []);
return (
<div className='snsShareContainer'>
<div>
<button
className='share-wrapper'
onClick={() => {
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${link}`,
'페이스북 공유하기',
'width=600,height=800,location=no,status=no,scrollbars=yes'
);
}}
>
<img className='facebook shareicon' src={facebook} />
</button>
</div>
<div>
<button
className='share-wrapper'
onClick={() => {
window.open(
`https://share.naver.com/web/shareView?url=${link}&title=네이버 공유하기`
);
}}
>
<img className='naver shareicon' src={naver} />
</button>
</div>
<div className='kakao-share-button'>
<button
className='share-wrapper'
onClick={() => shareKakao(link, '카카오 공유하기')}
>
<img className='kakao shareicon' src={kakao} alt={'Kakao Logo'} />
</button>
</div>
</div>
);
}
위코드를 정리하고 더 추가한 최종 전체코드(script-kakao SDK를 각 컴포넌트 useEffet로 불러올시)
(map을 활용하여 UI반복적인 것 축약)
import { useEffect } from 'react';
import facebook from '@assets/images/ico-user_facebook.png';
import naver from '@assets/images/naver.png';
import kakao from '@assets/images/ico-user-kakao.png';
import './snsShare.scss';
export default function SnsShare() {
const link = 'http://www.naver.com';
const link2 = 'http://localhost:3000/main';
const snsLists = [
{
name: 'faceBook',
src: facebook,
},
{
name: 'naver',
src: naver,
},
{
name: 'kakao',
//src: kakao, //내가 저장한 이미지 사용시
src: 'https://developers.kakao.com/assets/img/about/logos/kakaotalksharing/kakaotalk_sharing_btn_medium.png', //카카오톡 이미지 사용시
},
];
//faceBook
const shareFaceBook = () => {
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${link}`,
'페이스북 공유하기',
'width=600,height=800,location=no,status=no,scrollbars=yes'
);
};
//naver
const shareNaver = () => {
window.open(
`https://share.naver.com/web/shareView?url=${link}&title=네이버 공유하기`
);
};
//kakao
const shareKakao = () => {
if (window.Kakao === undefined) {
return;
}
if (window.Kakao) {
//카카오 스크립트가 로드된 경우
const kakao = window.Kakao;
//인증이 안되어있는 경우 인증한다.
if (!kakao.isInitialized()) {
kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); // 카카오에서 제공받은 javascript key를 넣어줌 -> .env파일에서 호출시킴
}
kakao.Share.sendDefault({
objectType: 'feed', // 카카오 링크 공유 여러 type들 중 feed라는 타입 -> 자세한 건 카카오에서 확인
content: {
title: 'title', // 인자값으로 받은 title(공유톡에서 나타남)
description: '설명 #딸기 #카페', // 인자값으로 받은 title(공유톡에서 나타남)
imageUrl:
'http://k.kakaocdn.net/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png', //공유받은 메시지 위에 뜨는 대표사진
link: {
mobileWebUrl: link2, // 인자값으로 받은 route(uri 형태)/카카오는 개발자에서 허용한 도메인만 가능
webUrl: link2,
},
},
/* social: {
likeCount: 286, //좋아요수
commentCount: 45, //댓글수
sharedCount: 845, //공유수
}, */ //안해도 상관 없음
buttons: [
{
title: '사이트로 이동',
link: {
mobileWebUrl: link2,
webUrl: link2,
},
},
/* {
title: 'title',
link: {
mobileWebUrl: route,
webUrl: route,
},
}, */ //2개버튼 만들기도 가능(버튼 클릭시 이동)
],
});
}
};
//kakao
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js';
script.async = true;
document.body.appendChild(script);
return () => document.body.removeChild(script);
}, []);
return (
<div className='snsShareContainer'>
{snsLists.map((item, i) => (
<button
className='share-wrapper'
onClick={
item.name === 'faceBook'
? shareFaceBook
: item.name === 'naver'
? shareNaver
: item.name === 'kakao'
? shareKakao
: ''
}
key={item.name}
>
<img className='shareicon' src={item.src} alt={`${item.name} logo`} />
</button>
))}
</div>
);
}
위에서 사용한 script태그를 동적으로 불러오는 방법은 아래 주소 참고
https://zindex.tistory.com/281
kakao SDK 를 public>index.html 에 넣어서 한번만 불러올 경우 전체코드
import { useEffect } from 'react';
import facebook from '@assets/images/ico-user_facebook.png';
import naver from '@assets/images/naver.png';
import kakao from '@assets/images/ico-user-kakao.png';
import './snsShare.scss';
import KakaoChannelBtn from './KakaoChannelBtn';
export default function SnsShare() {
const link1 = 'https://www.naver.com/'; //페이스북, 네이버는 localhost연결이 안돼서 네이버로 설정해둠
const link2 = 'http://localhost:3000/main';
const snsLists = [
{
name: 'faceBook',
src: facebook,
},
{
name: 'naver',
src: naver,
},
{
name: 'kakao',
//src: kakao, //내가 저장한 이미지 사용시
src: 'https://developers.kakao.com/assets/img/about/logos/kakaotalksharing/kakaotalk_sharing_btn_medium.png', //카카오톡 이미지 사용시
},
];
//faceBook
const shareFaceBook = () => {
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${link1}`,
'페이스북 공유하기',
'width=600,height=800,location=no,status=no,scrollbars=yes'
);
};
//naver
const shareNaver = () => {
window.open(
`https://share.naver.com/web/shareView?url=${link1}&title=네이버 공유하기`
);
};
//kakao
const shareKakao = () => {
if (window.Kakao) {
//카카오 스크립트가 로드된 경우
const kakao = window.Kakao;
//인증이 안되어있는 경우 인증한다.
if (!kakao.isInitialized()) {
kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); // 카카오에서 제공받은 javascript key를 넣어줌 -> .env파일에서 호출시킴
}
kakao.Share.sendDefault({
objectType: 'feed', // 카카오 링크 공유 여러 type들 중 feed라는 타입 -> 자세한 건 카카오에서 확인
content: {
title: 'title', // 인자값으로 받은 title(공유톡에서 나타남)
description: '설명 #딸기 #카페', // 인자값으로 받은 title(공유톡에서 나타남)
imageUrl:
'http://k.kakaocdn.net/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png', //공유받은 메시지 위에 뜨는 대표사진
link: {
mobileWebUrl: link2, // 인자값으로 받은 route(uri 형태)/카카오는 개발자에서 허용한 도메인만 가능
webUrl: link2,
},
},
/* social: {
likeCount: 286, //좋아요수
commentCount: 45, //댓글수
sharedCount: 845, //공유수
}, */ //안해도 상관 없음
buttons: [
{
title: '사이트로 이동',
link: {
mobileWebUrl: link2,
webUrl: link2,
},
},
/* {
title: 'title',
link: {
mobileWebUrl: route,
webUrl: route,
},
}, */ //2개버튼 만들기도 가능(버튼 클릭시 이동)
],
});
}
};
return (
<div className='snsShareContainer'>
{/* SNS 공유하기(faceBook,naver,kakao) */}
{snsLists.map((item, i) => (
<button
className='share-wrapper'
onClick={
item.name === 'faceBook'
? shareFaceBook
: item.name === 'naver'
? shareNaver
: item.name === 'kakao'
? shareKakao
: ''
}
key={item.name}
>
<img className='shareicon' src={item.src} alt={`${item.name} logo`} />
</button>
))}
{/* kakao 채널 추가 */}
<KakaoChannelBtn />
</div>
);
}
kakao SDK: public>index.html
<body>
..
<!-- kakao 채널추가 -->
<script
src="https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js"
integrity="sha384-dpu02ieKC6NUeKFoGMOKz6102CLEWi9+5RQjWSV0ikYSFFd8M3Wp2reIcquJOemx"
crossorigin="anonymous"
></script>
<script>
Kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); // 사용하려는 앱의 JavaScript 키 입력
</script>
</body>
</html>
TypeScript (위 코드에서 수정 있음)
위와 같이 코드를 작성후 TypeScript로 변환시 에러가 발생했다.
useEffect에서 script를 불러오는 코드에서의 오류이다.
수정 전 코드
'() => () => HTMLScriptElement' 형식의 인수는 'EffectCallback' 형식의 매개 변수에 할당될 수 없습니다. '() => HTMLScriptElement' 형식은 'void | Destructor' 형식에 할당할 수 없습니다. '() => HTMLScriptElement' 형식은 'Destructor' 형식에 할당할 수 없습니다. 'HTMLScriptElement' 형식은 'void | { [UNDEFINED_VOID_ONLY]: never; }' 형식에 할당할 수 없습니다. |
이 오류는 useEffect 훅의 첫 번째 인자로 함수를 전달하는데, 이 함수가 반환하는 값의 타입이 올바르지 않아서 발생하는 것이다. 위 코드에서 script를 생성하고 return하면서 body의 script태그를 지우는 것이 올바르지 않았다.
해결 방법으로는 useEffect 내부에서 생성한 스크립트 태그를 반환하는 대신, 이벤트 리스너를 사용하여 스크립트 로딩이 완료될 때 수행할 로직을 추가해주면 된다.
script.addEventListener 메서드를 사용하여 스크립트 로딩이 완료될 때 수행할 작업을 추가하고, useEffect 함수의 반환값으로 이벤트 리스너를 제거하는 함수를 반환하도록 수정했다.
addEventListener 가 더 궁금하다면? 잘 나와있는 사이트링크
수정한 코드
//kakao
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js'; //script 실행 src
script.async = true; //다운완료시 바로 실행
document.body.appendChild(script); //태그 생성 (크롬에서 확인 가능)
/* return () => document.body.removeChild(script); //계속 생기지 않게 중단 */
//에러 메시지 나타내는거라 없어도 작동됨
script.onerror = () => console.error('Failed to load Kakao SDK'); //스크립트 로드 실패 시 에러 메시지 출력
// 스크립트 로딩이 완료될 때 수행할 로직 (첫번째 인자인 load event는 정해져있는 것 꺼내씀/ 두번째 인자는 실행시키는 함수)
script.addEventListener('load', () => {
// Kakao SDK 로딩 완료 후 수행할 작업
window.Kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY);
});
// useEffect 함수의 반환값으로 이벤트 리스너를 제거하는 함수를 반환
return () => {
document.body.removeChild(script);
script.removeEventListener('load', () => {
// Kakao SDK 로딩 완료 후 수행할 작업
window.Kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY);
});
};
}, []);
위코드 포함한 TypeScript로 변환한 SnsShare.tsx 전체코드
import { useEffect } from 'react';
import facebook from '@assets/images/ico-user_facebook.png';
import naver from '@assets/images/naver.png';
import kakao from '@assets/images/ico-user-kakao.png';
import './snsShare.scss';
import KakaoChannelBtn from './KakaoChannelBtn';
interface SnsList {
name: string;
src: string;
}
export default function SnsShare() {
const link1 = 'https://www.naver.com/'; //페이스북, 네이버는 localhost연결이 안돼서 네이버로 설정해둠
const link2 = 'http://localhost:3000/main';
const snsLists: SnsList[] = [
{
name: 'faceBook',
src: facebook,
},
{
name: 'naver',
src: naver,
},
{
name: 'kakao',
//src: kakao, //내가 저장한 이미지 사용시
src: 'https://developers.kakao.com/assets/img/about/logos/kakaotalksharing/kakaotalk_sharing_btn_medium.png', //카카오톡 이미지 사용시
},
];
//faceBook
const shareFaceBook = () => {
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${link1}`,
'페이스북 공유하기',
'width=600,height=800,location=no,status=no,scrollbars=yes'
);
};
//naver
const shareNaver = () => {
window.open(
`https://share.naver.com/web/shareView?url=${link1}&title=네이버 공유하기`
);
};
//kakao
const shareKakao = () => {
//카카오 스크립트가 로드된 경우
const kakao = window.Kakao;
kakao.Share.sendDefault({
objectType: 'feed', // 카카오 링크 공유 여러 type들 중 feed라는 타입 -> 자세한 건 카카오에서 확인
content: {
title: 'title', // 인자값으로 받은 title(공유톡에서 나타남)
description: '설명 #딸기 #카페', // 인자값으로 받은 title(공유톡에서 나타남)
imageUrl:
'http://k.kakaocdn.net/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png', //공유받은 메시지 위에 뜨는 대표사진
link: {
mobileWebUrl: link2, // 인자값으로 받은 route(uri 형태)/카카오는 개발자에서 허용한 도메인만 가능
webUrl: link2,
},
},
/* social: {
likeCount: 286, //좋아요수
commentCount: 45, //댓글수
sharedCount: 845, //공유수
}, */ //안해도 상관 없음
buttons: [
{
title: '사이트로 이동',
link: {
mobileWebUrl: link2,
webUrl: link2,
},
},
/* {
title: 'title',
link: {
mobileWebUrl: route,
webUrl: route,
},
}, */ //2개버튼 만들기도 가능(버튼 클릭시 이동)
],
});
};
//kakao (SnsShare, KakaoChannelBtn에서 공통사용하고 있음)
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://t1.kakaocdn.net/kakao_js_sdk/2.1.0/kakao.min.js'; //script 실행 src
script.async = true; //다운완료시 바로 실행
document.body.appendChild(script); //태그 생성 (크롬에서 확인 가능)
/* script.onerror = () => console.error('Failed to load Kakao SDK'); //스크립트 로드 실패 시 에러 메시지 출력 */
// 스크립트 로딩이 완료될 때 수행할 로직 (첫번째 인자인 load event는 정해져있는 것 꺼내씀/ 두번째 인자는 실행시키는 함수)
script.addEventListener('load', () => {
// Kakao SDK 로딩 완료 후 수행할 작업(init:SDK초기화,키설정)
window.Kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY); //카카오에서 제공받은 javascript key를 넣어줌 -> .env파일에서 호출
});
// useEffect 함수의 반환값으로 이벤트 리스너를 제거하는 함수를 반환
return () => {
document.body.removeChild(script);
script.removeEventListener('load', () => {
// Kakao SDK 로딩 완료 후 수행할 작업
window.Kakao.init(process.env.REACT_APP_SHARE_KAKAO_LINK_KEY);
});
};
}, []);
return (
<div className='snsShareContainer'>
{/* SNS 공유하기(faceBook,naver,kakao) */}
{snsLists.map((item, i) => (
<button
className='share-wrapper'
onClick={
item.name === 'faceBook'
? shareFaceBook
: item.name === 'naver'
? shareNaver
: item.name === 'kakao'
? shareKakao
: undefined
}
key={item.name}
>
<img className='shareicon' src={item.src} alt={`${item.name} logo`} />
</button>
))}
{/* kakao 채널 추가 */}
<KakaoChannelBtn />
</div>
);
}
'React' 카테고리의 다른 글
[React] 카카오 채널 추가 버튼 구현하기 (feat.TypeScript) (0) | 2023.04.14 |
---|---|
[React] 자바스크립트 <script> 태그를 동적으로 불러오기 (0) | 2023.04.13 |
[React] StoryBook 배포하는 방법 (0) | 2023.04.11 |
[React] Story Book 사용법 (0) | 2023.04.11 |
[React] Story Book 사용법(Test하는 방법) (0) | 2023.04.10 |