본문 바로가기

React

[React] SNS 공유하기 구현 (feat.TypeScript)

링크를 상대방에게 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>

 

 

 

[React] 우리서비스를 더 널리, SNS 공유하기 (Navigator.share)

들어가며 이번에 갑자기 서비스를 운영 중에 팬덤을 타고 SNS에 서비스가 공유되는 일이 발생했었습니다. 그래서 이번 기회에 유저의 편의성을 위해 링크를 만들어 제공하거나 SNS로 바로 공유가

all-dev-kang.tistory.com

 


 네이버 공유하기

 

<button
    className='share-wrapper'
    onClick={() => {
      window.open(
        `https://share.naver.com/web/shareView?url=${link}&title=네이버 공유하기`
      );
    }}
>
	<img className='naver shareicon' src={naver} />
</button>

 

 

네이버 공유하기 개발가이드

NAVER Developers - 네이버 공유하기 개발가이드

developers.naver.com

 

 

[SNS 내보내기] 네이버 블로그 공유하기 API

지난 번 페이스북 공유하기에 이어서 이번 포스팅에서는 네이버 블로그 공유하기에 대해서 알아보겠습니다. 이미 페이스북 공유하기 포스팅을 보시고 오신 분이시라면 페이스북 공유하기에 비

marsland.tistory.com


카카오 공유하기

 

위에서는 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개버튼 만들기도 가능(버튼 클릭시 이동)
      ],
    });
  }
};

 

 

 

[React] 카카오 공유하기 기능

내 애플리케이션 등록플랫폼 등록내 애플리케이션을 등록하면 key들이 제공되는데, 그 중 javascript key를 사용하면 됨path: src > components > modal > Modal.jsx나의 경우 모달창을 띄우고 카카오톡 버튼을

velog.io

 

React Kakao 공유하기 기능

TripLog 프로젝트를 진행중에 카카오톡 공유하기 기능을 react에서 사용하기 위한 방법에 대한 내용이다.

velog.io

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com


전체 코드

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

 

[React] 자바스크립트 <script> 태그를 동적으로 불러오기

public폴더 의 index.html 파일의 바디태그 안의 하단에 를 추가해주면 해당 라이브러리를 적용할 수 있다. 하지만 이 방법을 사용할 경우, 자바스크립트를 항상 불러오기 때문에 필요한 컴포넌트에

zindex.tistory.com

 


 

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>
  );
}