React 개발을 하다 보면, 개발 환경, 로컬 환경, 운영 환경에 따라 API 주소나 각종 설정 값들이 달라져야 하는 경우가 많습니다. 이런 설정들을 코드에 직접 박아 넣으면 관리가 엉망이 되겠죠? 😥

그래서 오늘은 React 앱에서 env-cmd라는 멋진 도구를 사용하여 환경변수를 깔끔하게 관리하는 방법을 알려드리겠습니다! 마치 마법처럼, 설정 파일만 바꿔주면 앱이 알아서 환경에 맞는 설정을 적용해 줄 거예요. ✨

 

 

1단계: env-cmd 설치하기

먼저, 프로젝트에 env-cmd를 설치해야 합니다. 터미널을 열고 아래 명령어를 입력하세요:

npm install env-cmd --save

 

또는

yarn add env-cmd

 

 

이 명령어는 env-cmd를 프로젝트의 개발 의존성(devDependencies)으로 설치합니다. package.json 파일을 열어보시면 devDependencies 항목에 env-cmd가 추가된 것을 확인할 수 있습니다.

 

 

 

2단계: 환경변수 파일 만들기

이제 환경별로 설정 값들을 담을 파일을 만들어야 합니다. 프로젝트 루트 디렉토리에 다음 파일들을 만들어주세요:

  • .env.development: 개발 환경 설정
  • .env.local: 로컬 환경 설정 (개인 개발용)
  • .env.production: 운영 환경 설정 (실제 서비스용)

각 파일 안에는 환경변수들을 다음과 같은 형식으로 작성합니다.

REACT_APP_API_URL=https://dev.example.com/api
REACT_APP_FEATURE_ENABLED=true

주의! React 앱에서 사용하는 환경변수는 반드시 REACT_APP_으로 시작해야 합니다.

 

 

3단계: package.json 스크립트 수정하기

 

이제 package.json 파일의 scripts 부분을 수정하여 env-cmd를 사용하도록 설정합니다. 기존의 start 스크립트를 다음과 같이 변경하세요:

 
"scripts": {
    "start": "env-cmd -f .env.development react-scripts start",
    "start:local": "env-cmd -f .env.local react-scripts start",
    "start:prod": "env-cmd -f .env.production react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

 

 

스크립트 설정, 왜 이렇게 해야 할까요? 🤔

 

  • env-cmd: 이 명령어는 환경변수를 설정하고 다른 명령어를 실행하는 역할을 합니다.
  • -f: 이 옵션은 env-cmd에게 어떤 환경변수 파일을 사용할지 알려줍니다. -f .env.development는 .env.development 파일을 사용하겠다는 의미입니다.
  • react-scripts start: 이 명령어는 React 앱을 개발 모드로 실행합니다.

순서가 중요한 이유: env-cmd가 먼저 실행되어 환경변수를 설정해야 react-scripts start가 환경변수를 읽어서 앱에 적용할 수 있습니다. 만약 순서가 바뀌면 react-scripts start는 환경변수를 찾지 못해 제대로 동작하지 않을 수 있습니다.

이제 터미널에서 다음과 같이 명령어를 실행하여 앱을 실행할 수 있습니다:

  • 개발 환경: npm start 또는 yarn start
  • 로컬 환경: npm run start:local 또는 yarn start:local
  • 운영 환경: npm run start:prod 또는 yarn start:prod

 

 

4단계: 코드에서 환경변수 사용하기

 

React 컴포넌트 안에서 환경변수를 사용하려면 process.env 객체를 사용하면 됩니다. 예를 들어, API 주소를 사용하려면 다음과 같이 할 수 있습니다:

 
const apiUrl = process.env.REACT_APP_API_URL;

fetch(apiUrl + '/users')
  .then(response => response.json())
  .then(data => console.log(data));

 

-f 옵션, 왜 꼭 써야 할까요? 🤔

 

env-cmd는 기본적으로 .env 파일을 찾아서 환경변수를 설정합니다. 하지만 여러 환경에 따라 다른 설정 파일을 사용해야 하므로, -f 옵션을 사용하여 사용할 환경변수 파일을 명시적으로 지정해야 합니다. -f 옵션이 없다면 env-cmd는 어떤 파일을 사용해야 할지 알 수 없어 기본 설정으로 동작하거나 오류가 발생할 수 있습니다.

 

장점

  • 환경별 설정 분리: 환경변수 파일을 사용하여 각 환경에 맞는 설정을 쉽게 관리할 수 있습니다.
  • 코드 유지보수성 향상: 설정 값들이 코드에 직접 박혀 있지 않으므로 코드가 더 깔끔하고 유지보수하기 쉬워집니다.
  • 보안 강화: API 키나 비밀번호와 같은 중요한 정보는 코드에 직접 저장하지 않고 환경변수로 관리하여 보안을 강화할 수 있습니다.

 

마치며

이제 env-cmd를 사용하여 React 앱의 환경변수를 체계적으로 관리하는 방법을 알게 되었습니다. 이 방법을 사용하면 개발, 로컬, 운영 환경에 따라 설정 값을 쉽게 변경할 수 있고, 코드의 유지보수성과 보안성도 높일 수 있습니다.

 

 

React 개발을 하다 보면 useCallback이라는 훅을 심심치 않게 만나게 됩니다. "함수를 메모이제이션한다"는데, 이걸 왜 써야 하고 언제 써야 효과적일까요? 오늘은 useCallback의 필요성과 사용법을 쉽고 명확하게 알아보겠습니다.

 

함수가 새로 태어난다고? React 렌더링의 비밀

 

먼저 React 컴포넌트가 어떻게 동작하는지 떠올려 봅시다. 컴포넌트는 자신의 상태(state)나 부모로부터 받은 속성(props)이 변경되면 다시 렌더링됩니다. 이때 중요한 점은, 컴포넌트 함수 본문 전체가 다시 실행된다는 것입니다. 즉, 컴포넌트 내부에 정의된 함수들은 기본적으로 렌더링될 때마다 새로 만들어집니다.

function MyComponent() {
  const [count, setCount] = useState(0);

  // MyComponent가 리렌더링될 때마다 이 함수는 '새로운' 함수가 됩니다.
  const handleClick = () => {
    console.log("클릭!");
  };

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>카운트 증가</button>
      {/* 자식 컴포넌트에 함수를 props로 전달 */}
      <MyButton onClick={handleClick} />
    </div>
  );
}

// React.memo로 최적화된 자식 컴포넌트
const MyButton = React.memo(({ onClick }) => {
  console.log("자식 버튼 리렌더링!");
  return <button onClick={onClick}>자식 버튼</button>;
});

 

위 코드에서 "카운트 증가" 버튼을 누르면 MyComponent가 리렌더링되고, handleClick 함수도 새로 만들어집니다.

 

 

그래서 뭐가 문제인데? 불필요한 리렌더링!

 

MyButton 컴포넌트는 React.memo로 감싸져 있습니다. React.memo는 props가 이전 렌더링과 정확히 같다면(참조 동일성) 리렌더링을 건너뛰는 똑똑한 기능이죠.

하지만 handleClick 함수는 렌더링마다 새로 만들어지기 때문에, MyButton 입장에서는 onClick prop이 계속 **다른 값(다른 함수의 참조)**으로 전달되는 셈입니다. JavaScript에서 함수는 객체이고, 새로 만들어진 함수는 이전 함수와 내용이 똑같아도 다른 객체니까요.

결국, MyButton은 실제로는 아무것도 변하지 않았는데도 부모(MyComponent)가 리렌더링될 때마다 불필요하게 같이 리렌더링됩니다. (콘솔에 "자식 버튼 리렌더링!"이 계속 찍히는 것을 확인해보세요.)

 

구세주 등장! useCallback

 

useCallback은 바로 이 문제를 해결하기 위해 탄생했습니다.

  • useCallback(fn, deps)은 함수 fn 자체를 메모이제이션합니다.
  • 의존성 배열 deps 안의 값이 변경되지 않는 한, 함수를 새로 만들지 않고 이전에 만들었던 함수의 참조를 그대로 반환합니다.
function MyComponent() {
  const [count, setCount] = useState(0);

  // useCallback으로 handleClick 함수를 메모이제이션
  // 의존성 배열이 비어있으므로([]) 이 함수는 처음 마운트될 때만 생성되고,
  // 이후에는 계속 같은 참조를 유지합니다.
  const handleClick = useCallback(() => {
    console.log("클릭!");
    // 만약 함수 안에서 count 같은 상태를 사용한다면 deps에 넣어줘야 합니다!
    // 예: console.log(count); -> useCallback(..., [count]);
  }, []); // 의존성 배열

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>카운트 증가</button>
      <MyButton onClick={handleClick} /> {/* 이제 동일한 참조가 전달됨 */}
    </div>
  );
}

// 자식 컴포넌트 (React.memo 사용)
const MyButton = React.memo(({ onClick }) => {
  console.log("자식 버튼 리렌더링!");
  return <button onClick={onClick}>자식 버튼</button>;
});

 

이제 "카운트 증가" 버튼을 눌러도 handleClick 함수의 참조는 동일하게 유지됩니다. React.memo는 onClick prop이 변경되지 않았다고 판단하고, MyButton은 더 이상 불필요하게 리렌더링되지 않습니다! (콘솔에 "자식 버튼 리렌더링!"이 찍히지 않아요!)

 

언제 useCallback을 써야 할까?

 

  1. React.memo로 최적화된 자식 컴포넌트에 함수를 props로 전달할 때: 자식 컴포넌트의 불필요한 리렌더링을 막아 성능을 개선할 수 있습니다. (가장 흔한 사용 사례!)
  2. 함수를 다른 Hook(useEffect, useMemo)의 의존성 배열에 포함시킬 때: 함수의 참조가 안정적이지 않으면 해당 Hook이 불필요하게 계속 실행될 수 있습니다. useCallback으로 함수 참조를 안정화시키면 이를 방지할 수 있습니다. (위의 시간 버그 해결 사례에서도 사용되었죠!)

 

주의! 남용은 금물

useCallback 자체도 함수를 비교하고 메모리에 저장하는 약간의 비용이 듭니다. 따라서 모든 함수를 useCallback으로 감쌀 필요는 없습니다. 위에서 설명한 것처럼 **참조 동일성이 꼭 필요한 경우(성능 최적화가 필요한 경우)**에 사용하는 것이 좋습니다.

 

마무리

useCallback은 React 애플리케이션의 성능을 최적화하는 데 유용한 도구입니다. 함수의 참조 동일성을 유지해야 하는 상황을 잘 파악하고 적재적소에 활용한다면, 더 부드럽고 효율적인 사용자 경험을 제공할 수 있을 것입니다.

안녕하세요! 개발하다 보면 예상치 못한 버그와 마주치곤 하죠. 특히 '시간'과 관련된 로직은 자칫 잘못 다루면 까다로운 문제를 일으키기도 합니다. 오늘은 React 애플리케이션에서 실시간으로 변하는 정보를 처리하다가 발생했던 '무한 루프' 버그와 그 해결 과정을 공유해 보려고 합니다. (특정 서비스 코드는 제외하고 개념 중심으로 설명할게요!)

어떤 상황이었을까요?

사용자에게 현재 상점의 운영 상태("영업 중", "준비 중", "휴무일")를 실시간으로 보여줘야 하는 기능이 있었습니다. 이를 위해 다음과 같은 로직이 필요했죠.

 

사용자에게 현재 상점의 운영 상태("영업 중", "준비 중", "휴무일")를 실시간으로 보여줘야 하는 기능이 있었습니다. 이를 위해 다음과 같은 로직이 필요했죠.

  1. 서버 또는 상태 관리 라이브러리(Redux, Zustand 등)에서 상점의 영업 시작 시간, 종료 시간, 휴무일 정보를 가져옵니다.
  2. 현재 시간과 비교하여 상태를 결정합니다.
  3. 일정 간격(예: 1분마다)으로 현재 상태를 다시 확인하여 업데이트합니다. (영업 시작/종료 시간이 되면 자동으로 상태가 바뀌어야 하니까요!)

 

 

겉보기엔 간단해 보였지만, 이 로직은 "Maximum update depth exceeded"라는 무시무시한 에러를 뿜어내며 앱을 멈추게 만들었습니다. 😱

 

 

무엇이 문제였을까요?

바로 React의 렌더링 방식과 '시간' 객체의 특성을 제대로 이해하지 못한 데 있었습니다.

 

 

  1. 매번 새로 태어나는 '현재 시간': 컴포넌트가 렌더링될 때마다 moment()나 new Date() 같은 코드로 현재 시간을 가져오면, 매번 새로운 시간 객체가 만들어집니다. 내용상 같은 시간이라도 메모리 주소는 다른, 별개의 객체인 거죠.
  2. 불안정한 의존성: 이 '새로운' 시간 객체나, 이 객체를 기반으로 계산된 값(예: 영업 시작/종료 시간 moment 객체)을 useEffect나 useCallback의 의존성 배열에 넣으면 어떻게 될까요? React는 "어? 의존성이 바뀌었네!"라고 판단하고 해당 Hook을 매번 다시 실행합니다.

  3. 멈추지 않는 업데이트: 만약 useEffect 안에서 상태(state)를 변경하는 로직이 있다면? 상태 변경 -> 리렌더링 -> 새로운 시간 객체 생성 -> 의존성 변경 감지 -> useEffect 재실행 -> 상태 변경 -> ... 이렇게 무한 루프에 빠지게 되는 것입니다. 특히 주기적으로 상태를 체크하는 setInterval이나 커스텀 훅(useInterval)과 결합되면 문제는 더 심각해질 수 있습니다.

 

어떻게 해결했을까요?

핵심은 불필요한 재생성을 막고, 참조 동일성을 유지하는 것이었습니다.

 

  1. 계산된 값 안정화 (useMemo): 영업 시작/종료 시간처럼, 특정 입력값(스토어에서 가져온 시간 문자열)이 바뀌지 않는 한 변하지 않아야 하는 값은 useMemo를 사용해 감싸주었습니다. 이렇게 하면 입력값이 그대로일 때는 이전에 계산했던 moment 객체의 참조를 그대로 반환해줘서, 의존성 배열에서 안정적인 값으로 사용할 수 있습니다. (아래 예시)

  2. '현재 시간'은 필요할 때만: 컴포넌트 최상단에서 const now = moment();를 선언하는 대신, 실제로 현재 시간이 필요한 함수 내부 (예: 상태 체크 함수)에서 호출하도록 변경했습니다. 이렇게 하면 렌더링마다 새로운 now 객체가 생성되어 의존성을 불안정하게 만드는 문제를 피할 수 있습니다.

  3. 함수도 안정화 (useCallback): 상태를 체크하는 함수(checkOpen 같은)가 다른 useEffect의 의존성으로 사용되거나, 자식 컴포넌트에 props로 전달된다면 useCallback으로 감싸주는 것이 좋습니다. (자세한 내용은 아래 useCallback 설명에서!)

  4. 똑똑한 상태 업데이트: 상태를 업데이트할 때, 현재 값과 변경될 값이 같다면 굳이 업데이트할 필요가 없겠죠? setState의 함수형 업데이트를 사용하여 이전 상태(prevChip)와 비교 후, 변경이 필요할 때만 업데이트하도록 수정했습니다. (아래 예시)
  5. 휴무일 로직 분리: 휴무일인지 판단하는 로직과 영업시간을 체크하는 로직은 서로 다른 관심사입니다. 휴무일 체크는 주로 날짜가 바뀔 때 한 번만 하면 되므로, 별도의 useEffect로 분리하고, 휴무일일 경우 시간 체크 인터벌을 멈추도록(delay 상태를 null로 설정) 처리했습니다.

 

계산된 값 안정화 (useMemo)

const startTime = useMemo(() => {
  if (!storeHours.start) return null;
  return moment(storeHours.start, 'HHmm');
}, [storeHours.start]); // storeHours.start가 바뀔 때만 새로 계산

const endTime = useMemo(() => {
  // ... endTime 계산 로직 (자정 넘김 처리 포함) ...
}, [storeHours.end, startTime]); // storeHours.end나 startTime이 바뀔 때만 새로 계산

 

 

똑똑한 상태 업데이트

setChip(prevChip =>
  prevChip !== 'OPEN' ? 'OPEN' : prevChip
);

 

 

배운 점

  • React에서 시간처럼 계속 변하는 값을 다룰 때는 객체의 참조 동일성에 유의해야 합니다.
  • useMemo와 useCallback은 불필요한 계산과 렌더링을 막아주는 강력한 도구입니다. (하지만 남용은 금물!)
  • 상태 업데이트는 꼭 필요할 때만 하도록 최적화하는 것이 좋습니다.
  • 로직의 관심사를 분리하면 코드를 이해하고 관리하기 쉬워집니다.

혹시 비슷한 문제로 골머리를 앓고 계셨다면, 이 글이 작은 도움이 되었기를 바랍니다! 😊

예시

function MyBenefit(): React.ReactElement {
  const [inputCpnNm, setInputCpnNm] = useState('');
  
  return 
    <MyBenefitInput
            setInputCpnNm={setInputCpnNm}
            inputCpnNm={inputCpnNm}
    />
}

 

 

 이렇게 해도 되고

type Props = {
  inputCpnNm: string;
  setInputCpnNm: (e) => void;
};
function MyBenefitInput(props: Props): React.ReactElement {
 어쩌고저쩌고
}

 

 이렇게 해도 되고

(setInputCpnNm 위에 마우스 올리면 타입 알려줌)

import React, { Dispatch, SetStateAction } from 'react';

type Props = {
  inputCpnNm: string;
  setInputCpnNm: Dispatch<SetStateAction<string>>;
};
function MyBenefitInput(props: Props): React.ReactElement {
 어쩌고저쩌고
}

 

 

e.stopPropagation();

 

 

 

 

 

 

https://pa-pico.tistory.com/20

 

[개념잡기] e.preventDefault() 와 stopPropagation() 의 차이

stopPropogation vs preventDefault e.preventDefault()와 e.stopPropagation()의 차이 두개의 코드 모두 이벤트 관련 동작에서 많이 사용되는 코드이다. 둘의 차이점은 무엇일까 알아보자. e.preventDefault() html 에서 a

pa-pico.tistory.com

 

mutate: (variables: TVariables, { onSuccess, onSettled, onError }) => void

 

The mutation function you can call with variables to trigger the mutation and optionally hooks on additional callback options.

변수를 사용하여 호출할 수 있는 mutation 함수는 mutation을 유발하고 추가 콜백 옵션을 선택적으로 연결할 수 있습니다.

 

 

 

🤓 variables

variables: TVariables

 

  • Optional(선택적)
  • mutateFn에 전달할 객체

 

 

🤓 onSuccess

onSuccess: (data: TData, variables: TVariables, context: TContext) => void

 

  • Optional(선택적)
  • 이 함수는 mutation함수가 성공하면 실행되고 mutation 결과데이터가 전달됩니다. 
  • Void 함수의 경우 반환된 값은 무시됩니다.

 

 

🤓 onError

onError: (err: TError, variables: TVariables, context: TContext | undefined) => void

 

  • Optional(선택적)
  • 이 함수는 mutation함수가 실패하면 실행되고 오류가 전달됩니다. 
  • Void 함수의 경우 반환된 값은 무시됩니다.

 

 

🤓 onSettled

onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => void

 

  • Optional(선택적)
  • 이 함수는 mutation함수가 성공하거나 실패하거나 둘 다 실행되고 mutation 결과데이터나 오류가 전달됩니다. 
  • Void 함수의 경우 반환된 값은 무시됩니다.

 

 

🤓 응용

const apiGetUpdateMutate = useMutation(apiGetUpdate, {
    onSuccess: (data, variables) => {
      const type = variables.type;
      const dvs = variables.dvs;

      if (type === PUSH) {
        //PUSH 값 변경 시 앱에 저장
        const pushvDvs =
          Data.list.filter(
            data => data.type === PUSH,
          )[0]?.dvs || '0';
        if (pushvDvs !== dvs)
          bridgeSetSettingInfo('PYN', dvs === '1' ? 'Y' : 'N');
      }
    },
    onError: (error, variables) => {
      if (variables.type === PUSH) {
        alert(
          '오류가 발생하였습니다. 잠시 후 다시 시도해주세요.',
        );
      }
    },
    onSettled: (data, error, variables) => {
      if (variables.type === PUSH) {
        queryClient.invalidateQueries([E_QUERY_KEY.LIST_YN, 'event']);
      }
      setIsInputDisabled(false); //더블클릭 방지
    },
  });

 

 

https://tanstack.com/query/latest/docs/framework/react/reference/useMutation

🤓useRef 언제 사용하지?

1️⃣  저장공간

ref는 state와 비슷하게 어떤 값을 저장하는 저장공간으로 사용된다.

State의 변화 ➡️ 렌더링 ➡️ 컴포넌트 내부 변수들 초기화
Ref의 변화 ➡️ No 렌더링 ➡️ 변수들의 값이 유지됨
State의 변화 ➡️ 렌더링 ➡️ 그래도 Ref의 값은 유지됨

 변경시 렌더링을 발생시키지 말아야하는 값을 다룰때 사용한다

(변화는 감지해야하지만, 그 변화가 렌더링을 발생시키면 안될때!!)

 

 

2️⃣  DOM요소에 접근

✅ useRef를 사용하면 손쉽게 input에 접근할 수 있다.

바닐라 자바스크립트의 getElementById, querySelector와 비슷하다.

 

<DOM요소 접근의 대표적인 예>

대표적으로 Input요소를 클릭하지 않아도 focus를 줄때 사용

 

 

 

🤓응용

 

우선 useRef import하기 (useState도 활용할거라 같이 import)

import React, { useRef, useState } from 'react';

 

 

변수 생성, ustState로 상태관리 

const foldRef = useRef<HTMLDivElement>(null);
const [foldStore, setFoldStore] = useState<boolean>(false);

 

 

button에 onClick으로 함수 적용

아래 접었다폈다 할 부분에 foldRef 참조

<button
  title="클릭으로 접고 펴는 버튼"
  onClick={() =>
    onFoldItem(foldRef, foldStore, setFoldStore)
  }
>

<dd className="content" ref={foldRef}>어쩌고 접었다폈다할 내용~</dd>

 

 

style.css

  .content {
    max-height: 0px;
    overflow: hidden;
    transition: max-height 0.3s ease-out;
  }

 

 

함수

/**
 * 목록 toggle(접기/펼치기)
 * @description 이벤트 내역보기 클릭 시 사용
 */
export const onFoldItem = (foldRef, foldStore, setFoldStore) => {
  if (!foldRef || !foldRef.current) {
    return;
  }

  const style = foldRef.current.style;

  if (foldStore) {
    style.maxHeight = '0';
  } else if (!foldStore) {
    style.maxHeight = `${foldRef.current.scrollHeight}px`;
  }
  setFoldStore(!foldStore);
};

 

 

 

https://hihiha2.tistory.com/19

가입 후 애플리케이션 등록하기

 

주소
https://developers.kakao.com/console/app

 

카카오계정

 

accounts.kakao.com

 

 

애플리케이션 등록

1. 내 애플리케이션 들어가서 애플리케이션 추가하기

 

 

 

등록 예시

 

2. 플랫폼 등록하기

애플리케이션 추가하기

 

등록 예시 화면

 

등록된 화면

 

앱 키 화면

 

 

env 파일에 앱 키 등록

JavaScript 키 등록

 

 

index.html에 script 연결

 

<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>

 

 

App.jsx에 kakao 초기 설정

console로 확인해보기

  /**
   * @name kakao 설정
   */
  useEffect(() => {
    if (process.env.REACT_APP_KAKAO_KEY) {
      window.Kakao.init(process.env.REACT_APP_KAKAO_KEY);
      console.log(window.Kakao.isInitialized())
    }
  }, []);

 

초기화가 잘 되어 API를 가져왔는지 콘솔로 확인해보면 true값이 뜨고, false라면 초기화를 실패

 

함수 설정

  const shareKakao = () => {
    window.Kakao.Link.sendDefault({
      objectType: "feed",
      content: {
        title: "롯데마트 전단에 초대합니다.",
        description: "이하동문",
        imageUrl: `${IMAGE_PATH}/common/btn_share_gray.png`,
        link: {
          mobileWebUrl: process.env.REACT_APP_GO_URL,
        },
      },
      buttons: [
        {
          title: "함께 보기",
          link: {
            mobileWebUrl: process.env.REACT_APP_GO_URL,
          },
        },
      ],
    });
  };

 

        <button onClick={shareKakao}>
          <img
            src="https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png"
            alt="카카오링크 보내기 버튼"
          />
        </button>

 

 

https://velog.io/@da__hey/React-React-Typescript%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%94%8C%EB%9E%AB%ED%8F%BC-API-%EC%9D%B4%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0

 

[ React ] React, Typescript를 통해 카카오톡 메시지 플랫폼 API 이용해보기

카카오톡 메시지 플랫폼 API 이용해보기 💬 들어가며 사이드 프로젝트를 진행하면서 사용자 초대 링크를 카카오톡 공유 API를 통해 전송하는 기능을 구현하게 되었다. API에서 데이터를 가져오는

velog.io

 

+ Recent posts