카카오톡 공유하기 콜백 기능을 통해 전송 여부 확인하기(with 폴링)

지누·2024년 12월 8일
post-thumbnail

카카오톡 공유하기 기능은 구현이 되었다고 가정하고, 카카오톡 공유하기 콜백 기능을 사용하는 법을 포스팅한다.

0. 카카오톡 공유 콜백이란?

카카오톡 공유하기 기능을 사용하면 다른 사람에게 카카오톡 메시지를 보낼 수 있다. 이것은 프론트엔드에서 이루어지는 과정이라, 서버에서 공유하기가 실제로 이루어졌는지 알 방법이 없다.

우리 서비스에서는 칭찬 메시지를 공유할 때 SendPraise엔티티를 생성하고 고유한 uuid값을 가지게 한다. 그러나 버그나 이탈로 인해 전송이 이루어지지 않은 경우 SendPraisesendStatus값을 이용하여 관리하도록 하였다.

이것을 가능하게 하려면, 실제 공유가 되었는지 우리 서비스 서버에서 알 수 있어야 한다. 카카오톡 공유하기에서는 카카오톡 공유 완료 정보를 콜백을 통해 제공한다.(카카오 서버 -> 서비스 서버)

1. [Kakao dev] 카카오톡 공유 콜백 api 설정하기

참고 : https://developers.kakao.com/docs/latest/ko/message/prerequisite#set-kakaotalk-sharing-callback

  1. [내 애플리케이션] > [메시지] > [카카오톡 공유 콜백] 메뉴에서 [콜백 등록] 버튼을 누릅니다.
  2. 카카오 서버로부터 알림을 받을 서비스 서버의 콜백(Callback) URL과 요청 방법(Method)을 설정합니다.
  3. 구현하려는 플랫폼별 카카오톡 공유 개발가이드를 참고하여 사용자 정의 파라미터를 설정합니다.

위 사진처럼, 카카오톡 공유하기가 완료되었을 때 우리 서비스 서버로 요청을 보낼 수 있도록, 콜백 URL을 등록 해 놓는다.

카카오 서버에서 해당 api주소로 GET/POST 요청을 보내는 것이기 때문에, aws 등에서 ip 화이트리스트에 추가해 주는 것을 고려해야 한다.

2. [Backend] 콜백 api를 받을 수 있도록 구현하기

2.1. 이용정책 살펴보기

이용정책을 꼭 읽으라고 되어있는데 정상적으로 동작하지 않는다면 알아두면 좋을 것 같다.

  • 200 OK로 응답을 보내야 하고, 카카오 서버의 IP를 알아두자.

2.2. 콜백받는 api 구현하기

참고 : https://developers.kakao.com/docs/latest/ko/message/callback#success

콜백 api는 GET 또는 POST 방식만 가능하다. 또한 카카오톡에서 콜백 api를 보낼 때 헤더와 파라미터로 많은 정보를 추가로 보내주는데, 이것을 받을 수 있도록 구현해야 한다.

2.3. 콜백받는 api의 헤더

  • Authorization
    - 헤더를 통해 받을 수 있고, 이것은 프론트엔드에서 .env로 가지고 있다가 넘겨주는 것이므로 아래의 프론트엔드쪽 코드를 참고하자.
  • X-Kakao-Resource-ID
    - 콜백별로 가지는 유니크 ID이다. 이것을 어떻게 활용할 지는 잘 모르겠다.
  • User-Agent
    - 카카오서버에서 보낸 것을 알리는 값이다.

2.4. 콜백받는 api의 파라미터(GET/POST)

  • CHAT_TYPE
    - 채팅방 타입을 알 수 있다. 이것을 활용하여, 1대1 채팅에 전송한 공유만 유효하게 하는 방식을 적용 가능하다.
  • HASH_CHAT_ID
    - 서비스별로 유일한 채팅방의 유니크 값이다. 이것을 활용하면 같은 채팅방에 공유하는 것을 막을 수 있을 것 같다.
    - 카뱅에서 최근 진행한 빼빼로뽑기 이벤트가 이 값을 이용한 듯 하다.
  • TEMPLATE_ID
    - 카카오톡 공유하기 메시지의 템플릿을 사용한 경우 사용하는 값이다. 우리 서비스도 템플릿을 사용하므로, RequestDto에 추가할 것이다.
  • 사용자 지정 파라미터(key-value)
    - 프론트엔드에서 serverCallbackArgs를 이용하여 원하는 파라미터를 key-value형태로 보낼 수 있다.
    - 우리 서비스에서는, praiseUuid : uuid 형태로 보내는 것을 사용할 것이다.

콜백 api의 파라미터 응답 예시

콜백받는 api를 GET으로 구현한다면

https://api.dodok-honeypot.com/api/send-praise/checkCHAT_TYPE=MemoChat&HASH_CHAT_ID=${HASH_CHAT_ID}&TEMPLATE_ID=10000&custom_parameter_key=custom_parameter_value"

위처럼 받게 될 것이고,

콜백받는 api를 POST로 구현한다면 RequestBody를 통해

{
            "CHAT_TYPE":"MemoChat",
            "HASH_CHAT_ID":"${HASH_CHAT_ID}",
            "TEMPLATE_ID":10000,
            "custom_parameter_key":"custom_parameter_value"
}

처럼 받게 될 것이다.

2.5. 스프링부트에서 콜백받을 api를 구현한 코드

Controller

//SendPraiseController.java
/**
 * 칭찬이 성공적으로 전송되었는지 확인할 수 있는 카카오 콜백 api
 */
@PostMapping("/check")
public ResponseEntity<SuccessResponse<?>> checkSendPraiseSent(
            @RequestHeader("Authorization") String authrozitaion,
            @RequestHeader("X-Kakao-Resource-ID") String kakaoResourceId,
            @RequestHeader("User-Agent") String userAgent,
            @RequestBody CheckSendPraiseSentReqDto req
) {
        checkSendPraiseSentService.execute(req);
        return SuccessResponse.ok(null);

}

CheckSendPraiseSentReqDto(Http Request Body)

//CheckSendPraiseSentReqDto.java
public record CheckSendPraiseSentReqDto(
        String CHAT_TYPE,   // 채팅방 유형 (MemoChat, DirectChat 등)
        String HASH_CHAT_ID, // 채팅방 참고용 ID
        String TEMPLATE_ID,  // 사용된 메시지 템플릿 ID
        String praiseUuid    // 칭찬 메시지의 UUID
) {
}

Service

콜백api를 통해 채팅방 유형을 전달받으면 해당 메세지의 SendStatus를 변경시켜주는 로직을 구현하였다.

// CheckSendPraiseSentService
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class CheckSendPraiseSentService {
    public static final String MEMO_CHAT = "MemoChat"; // 나와의 채팅방
    public static final String DIRECT_CHAT = "DirectChat"; // 다른 사용자와의 1:1 채팅방
    public static final String MULTI_CHAT = "MultiChat"; // 다른 사용자들과의 그룹 채팅방
    public static final String OPEN_DIRECT_CHAT = "OpenDirectChat"; // 1:1 오픈채팅방
    public static final String OPEN_MULTI_CHAT = "OpenMultiChat"; // 그룹 오픈채팅방4

    private final SendPraiseHelper sendPraiseHelper;
    public void execute(CheckSendPraiseSentReqDto req) {
        SendPraise sendPraise = sendPraiseHelper.findByUuidOrElseThrow(req.praiseUuid());
        SendStatus newStatus = switch (req.CHAT_TYPE()) {
            case MEMO_CHAT -> SendStatus.MYSELF;
            case DIRECT_CHAT, OPEN_DIRECT_CHAT -> SendStatus.DIRECT;
            case MULTI_CHAT, OPEN_MULTI_CHAT -> SendStatus.GROUP;
            default -> SendStatus.FAIL;
        };

        sendPraise.updateSendStatus(newStatus);

        log.info("[kakao-talk callback api] requestBody praiseUuid: " + req.praiseUuid());
        log.info("[kakao-talk callback api] requestBody CHAT_TYPE: " + req.CHAT_TYPE());
        log.info("[kakao-talk callback api] requestBody HASH_CHAT_ID: " + req.HASH_CHAT_ID());
    }
}

2.6.(추가) 카카오톡 공유 완료 확인 폴링 api

프론트에서 카카오톡 공유하기 후, 1초에 한번씩 공유 완료 여부를 확인하는 폴링 api이다.
그냥 DB에 접근해서 SendStatus 값을 가져오는 api이다.

Controller

@GetMapping("/check")
    public ResponseEntity<SuccessResponse<?>> getSendPraiseSent(
            @RequestParam(name = "praise-uuid") String praiseUuid
    ) {
        GetSendPraiseSentResDto res = getSendPraiseSentService.execute(praiseUuid);
        return SuccessResponse.ok(res);
    }

Service

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class GetSendPraiseSentService {
    private final SendPraiseHelper sendPraiseHelper;
    private final SendPraiseMapper sendPraiseMapper;
    public GetSendPraiseSentResDto execute(String praiseUuid) {
        SendPraise sendPraise = sendPraiseHelper.findByUuidOrElseThrow(praiseUuid);
        return sendPraiseMapper.toGetSendPraiseSentResDto(sendPraise);
    }
}

3.[Frontend] 카카오톡 공유하기에 콜백 파라미터 추가하기

카카오톡 공유하기 기능을 구현했다면, serverCallbackArgs만 추가해주면 된다.
사용자지정 파라미터가 필요없으면 넣지 않아도 된다.

3.1. 카카오톡 공유하기 코드에 콜백파라미터 추가하기

//ComplimentSendContentPage.tsx

/* 2. 카카오 공유 */
if (isKakaoLoaded) {
  const { Kakao, location } = window;
  Kakao.Share.sendScrap({
    requestUrl: location.origin + location.pathname,
    templateId: 114602,
    templateArgs: {
      senderName: sender,
      receiverName: receiverName,
      content: content,
      id: generatedUuid,
    },
    serverCallbackArgs: { praiseUuid: generatedUuid }, 
   );
  }
 }

이 기능을 활용한다면, 토스나 카뱅에서 진행하는 공유하기를 통해 뽑기 기회 얻기 기능을 만들 수 있을 것 같다.

백엔드 PR 내용
프론트 코드

profile
열심히 살자😱

0개의 댓글