카톡으로 페이지를 공유하는 버튼을 웹사이트에 달고 싶은데, 공식 문서를 봐도 JavaScript 예제만 있고 PHP 백엔드에서 어떻게 처리해야 하는지 감이 잡히지 않는 경험을 해봤을까. 대부분의 개발자들은 프론트엔드의 카톡 공유 SDK만 설정하고, 서버에서 실제로 전송되는 데이터나 검증 로직을 제대로 구현하지 못한 채로 넘어간다. 이번에는 카카오톡 공유 API의 정확한 동작 원리는 물론, PHP 백엔드에서 공유 메타데이터를 동적으로 생성하고, 사용자 인증을 처리하고, 서버 로그로 공유 통계를 추적하는 방법까지 완벽하게 정리해서 소개하겠다.
카카오톡 공유 기능은 단순해 보이지만 내부 동작은 생각보다 복잡하다. 사용자가 '공유' 버튼을 클릭하면 JavaScript SDK가 카카오 인증 서버에 현재 페이지 정보(URL, 메타데이터)를 전달하고, 카카오는 이를 카톡 메시지로 변환해서 사용자의 카톡 계정을 통해 친구나 그룹에 발송한다. 여기서 중요한 건 공유될 메타데이터가 정적(static)이 아니라 동적(dynamic)이어야 한다는 점이다.
예를 들어 상품 상세 페이지마다 다른 이미지와 제목으로 공유되어야 하는데, 이를 위해서는 PHP에서 Open Graph(OG) 메타 태그를 페이지 렌더링 시점에 실시간으로 생성해야 한다. 카카오톡은 공유될 때 해당 페이지의 og:title, og:description, og:image를 읽어서 메시지 미리보기를 생성하기 때문이다. 또한 카카오 개발자 센터에서 발급받은 JavaScript 앱 키(App Key)도 필수다.
먼저 카카오 개발자 센터에서 애플리케이션을 등록해야 한다. 여기서 주의할 점은 '카카오톡 공유'를 쓸 때 두 가지 키가 필요하다는 것이다. 첫째는 JavaScript 앱 키(App Key)로, 프론트엔드에서 SDK를 초기화할 때 쓴다. 둘째는 REST API 키(REST API Key)로, 서버에서 사용자 정보나 공유 통계를 조회할 때 쓴다(선택사항이지만 실무에선 거의 필수).
개발자 센터 대시보드에서 앱 → '설정' → '플랫폼'을 들어가 웹 URL을 등록하고, '카카오 로그인'에서 '동의항목'을 설정해야 한다. 공유만 할 거라면 추가 동의항목은 필요 없지만, 나중에 사용자 정보(닉네임, 프로필 사진)까지 연동할 계획이 있다면 미리 동의항목을 활성화해두는 게 좋다.
가장 간단한 경우부터 시작하자. 웹사이트 전체에서 동일한 메타데이터로 공유하는 경우다.
프론트엔드 HTML(한 번만 로드):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>우리 서비스</title>
<meta property="og:title" content="우리 서비스에 가입하세요!" />
<meta property="og:description" content="최고의 경험을 제공합니다" />
<meta property="og:image" content="https://example.com/og-image.jpg" />
<meta property="og:url" content="https://example.com" />
</head>
<body>
<button id="kakaoShareBtn">카톡으로 공유</button>
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
<script>
// Kakao SDK 초기화 (JavaScript 앱 키 필수)
Kakao.init('YOUR_JAVASCRIPT_APP_KEY');
document.getElementById('kakaoShareBtn').addEventListener('click', function() {
Kakao.Share.sendDefault({
objectType: 'feed',
content: {
title: '우리 서비스에 가입하세요!',
description: '최고의 경험을 제공합니다',
imageUrl: 'https://example.com/og-image.jpg',
link: {
mobileWebUrl: 'https://example.com',
webUrl: 'https://example.com'
}
},
buttons: [
{
title: '앱 설치',
link: {
mobileWebUrl: 'https://example.com/download',
webUrl: 'https://example.com/download'
}
}
]
});
});
</script>
</body>
</html>
동작 방식: 사용자가 '카톡으로 공유' 버튼을 클릭하면 Kakao.Share.sendDefault()가 실행되고, 정의된 objectType(feed)과 content가 카톡 메시지로 변환되어 사용자의 카톡에 팝업으로 띄워진다. 친구를 선택하면 바로 전송된다.
실무에서는 상품 상세 페이지, 블로그 글, 프로필 페이지 등 URL마다 다른 메타데이터로 공유해야 한다. 이 경우 PHP에서 Open Graph 메타 태그를 동적으로 생성해야 한다.
PHP 백엔드 (product.php):
<?php
// 상품 ID를 URL 파라미터에서 추출
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$product_id) {
header('HTTP/1.1 404 Not Found');
exit('상품을 찾을 수 없습니다');
}
// 데이터베이스에서 상품 정보 조회
// (실제로는 PDO 또는 MySQLi를 사용)
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'user', 'password');
$stmt = $pdo->prepare('SELECT id, title, description, image_url, price FROM products WHERE id = ?');
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
header('HTTP/1.1 404 Not Found');
exit('상품을 찾을 수 없습니다');
}
// Open Graph 메타 태그 생성 (HTML 헤더에 출력할 변수)
$og_title = htmlspecialchars($product['title'], ENT_QUOTES, 'UTF-8');
$og_description = htmlspecialchars(substr($product['description'], 0, 100), ENT_QUOTES, 'UTF-8');
$og_image = htmlspecialchars($product['image_url'], ENT_QUOTES, 'UTF-8');
$og_url = 'https://example.com/product.php?id=' . $product_id;
$javascript_app_key = 'YOUR_JAVASCRIPT_APP_KEY';
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><?php echo $og_title; ?></title>
<!-- 카톡 공유용 메타 태그 -->
<meta property="og:title" content="<?php echo $og_title; ?>" />
<meta property="og:description" content="<?php echo $og_description; ?>" />
<meta property="og:image" content="<?php echo $og_image; ?>" />
<meta property="og:url" content="<?php echo $og_url; ?>" />
<meta property="og:type" content="product" />
</head>
<body>
<h1><?php echo $og_title; ?></h1>
<p>가격: <?php echo number_format($product['price']); ?>원</p>
<img src="<?php echo $og_image; ?>" alt="상품 이미지" />
<p><?php echo htmlspecialchars($product['description'], ENT_QUOTES, 'UTF-8'); ?></p>
<button id="kakaoShareBtn">카톡으로 공유하기</button>
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
<script>
Kakao.init('<?php echo $javascript_app_key; ?>');
document.getElementById('kakaoShareBtn').addEventListener('click', function() {
Kakao.Share.sendDefault({
objectType: 'product',
content: {
title: '<?php echo addslashes($og_title); ?>',
description: '<?php echo addslashes($og_description); ?> - <?php echo number_format($product['price']); ?>원',
imageUrl: '<?php echo $og_image; ?>',
link: {
mobileWebUrl: '<?php echo $og_url; ?>',
webUrl: '<?php echo $og_url; ?>'
}
},
buttons: [
{
title: '구매하기',
link: {
mobileWebUrl: '<?php echo $og_url; ?>',
webUrl: '<?php echo $og_url; ?>'
}
}
]
});
});
</script>
</body>
</html>
동작 방식: 사용자가 product.php?id=123을 방문하면, PHP가 데이터베이스에서 해당 상품 정보를 조회해서 og: 메타 태그를 생성한다. 카톡 공유 SDK는 이 메타 태그를 읽어서 공유 미리보기를 만든다. 중요한 건 og:image URL이 외부에서 접근 가능한 절대 URL(https:// 포함)이어야 한다는 점이다.
실무에서는 누가 언제 어떤 상품을 공유했는지 추적하고 싶을 때가 있다. 이를 위해 프론트엔드에서 공유 이벤트가 발생할 때 백엔드에 AJAX 요청을 보내서 로그를 기록할 수 있다.
PHP 백엔드 (log_share.php):
<?php
header('Content-Type: application/json; charset=UTF-8');
// POST 요청인지 확인
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'POST 요청만 허용됩니다']);
exit;
}
// JSON 바디 파싱
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['product_id']) || !isset($input['user_id'])) {
http_response_code(400);
echo json_encode(['error' => 'product_id와 user_id는 필수입니다']);
exit;
}
$product_id = (int)$input['product_id'];
$user_id = (int)$input['user_id'];
$shared_at = date('Y-m-d H:i:s');
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$ip_address = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
// 데이터베이스에 공유 로그 기록
try {
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'user', 'password');
$stmt = $pdo->prepare(
'INSERT INTO share_logs (product_id, user_id, shared_at, user_agent, ip_address)
VALUES (?, ?, ?, ?, ?)'
);
$stmt->execute([$product_id, $user_id, $shared_at, $user_agent, $ip_address]);
echo json_encode(['success' => true, 'message' => '공유 로그가 기록되었습니다']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'DB 오류: ' . $e->getMessage()]);
}
?>
프론트엔드 JavaScript (공유 추적):
<script>
Kakao.init('YOUR_JAVASCRIPT_APP_KEY');
const productId = new URLSearchParams(window.location.search).get('id');
const userId = <?php echo isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0; ?>;
document.getElementById('kakaoShareBtn').addEventListener('click', function() {
Kakao.Share.sendDefault({
objectType: 'product',
content: {
title: '상품 제목',
imageUrl: 'https://example.com/image.jpg',
link: {
mobileWebUrl: 'https://example.com/product.php?id=' + productId,
webUrl: 'https://example.com/product.php?id=' + productId
}
}
});
// 공유 로그 기록 (비동기 요청)
fetch('/log_share.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ product_id: productId, user_id: userId })
})
.then(res => res.json())
.then(data => console.log('공유 로그 저장됨:', data))
.catch(err => console.error('로깅 실패:', err));
});
</script>
이렇게 하면 사용자가 공유할 때마다 share_logs 테이블에 기록이 남는다. 나중에 admin 페이지에서 상품별 공유 통계를 조회할 수 있다.
✗ 잘못된 것: og:image에 상대 경로 사용
<!-- 잘못된 예 -->
<meta property="og:image" content="/images/product.jpg" />
<!-- 왜 안 되나: 카톡 서버가 이 상대 경로를 해석하지 못함 -->
✓ 올바른 것: og:image에 절대 URL 사용
<!-- 올바른 예 -->
<meta property="og:image" content="https://example.com/images/product.jpg" />
<!-- CDN 사용 시: https://cdn.example.com/images/product.jpg -->
✗ 잘못된 것: og:description에 HTML 태그 포함
$og_description = '상품 설명: <strong>할인중</strong>';
echo '<meta property="og:description" content="' . $og_description . '" />';
// 결과: og:description 값에 HTML 태그가 그대로 들어감 → 카톡에서 깨짐
✓ 올바른 것: strip_tags()로 HTML 제거
$og_description = strip_tags('상품 설명: <strong>할인중</strong>');
echo '<meta property="og:description" content="' . htmlspecialchars($og_description, ENT_QUOTES, 'UTF-8') . '" />';
// 결과: 'og:description="상품 설명: 할인중"' → 정상 작동
✗ 잘못된 것: og:image 크기 고려하지 않음
카톡에서 권장하는 og:image 크기는 1200x630px이다. 너무 작거나 큰 이미지는 미리보기에서 찌그러지거나 로딩 지연을 일으킨다.
✓ 올바른 것: 적절한 크기의 이미지 사용 + 크기 명시
<meta property="og:image" content="https://example.com/images/product-1200x630.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
✗ 잘못된 것: 카카오 SDK 두 번 초기화
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
<script>
Kakao.init('KEY1');
Kakao.init('KEY2'); // 두 번 초기화 시 예측 불가능한 동작
</script>
✓ 올바른 것: 한 번만 초기화 + 상태 체크
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
<script>
if (!Kakao.isInitialized()) {
Kakao.init('YOUR_JAVASCRIPT_APP_KEY');
}
// 이후 공유 로직...
</script>
✗ 잘못된 것: 사용자 입력값을 og:title에 직접 넣기
$user_input = $_GET['title']; // 사용자 입력
echo '<meta property="og:title" content="' . $user_input . '" />';
// XSS 취약점 발생 가능
✓ 올바른 것: htmlspecialchars()로 이스케이프
$user_input = $_GET['title'];
echo '<meta property="og:title" content="' . htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8') . '" />';
// 안전한 HTML 속성값
데이터베이스에서 매번 조회하면 공유 시마다 쿼리가 발생한다. 이를 최적화하려면 PHP Opcache나 Redis를 활용해서 메타데이터를 캐싱할 수 있다.
<?php
function getProductMetadata($product_id, $pdo) {
$cache_key = 'product_meta_' . $product_id;
// Redis에서 캐시 확인
$redis = new Redis();
$redis->connect('localhost', 6379);
$cached = $redis->get($cache_key);
if ($cached) {
return json_decode($cached, true);
}
// 캐시 미스 시 DB에서 조회
$stmt = $pdo->prepare('SELECT id, title, description, image_url FROM products WHERE id = ?');
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if ($product) {
// 1시간 동안 캐시 저장
$redis->setex($cache_key, 3600, json_encode($product));
}
return $product;
}
// 사용 예
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'user', 'password');
$product = getProductMetadata($_GET['id'], $pdo);
?>
카카오톡 공유 API는 결국 메타데이터 전달 메커니즘이다. og: 태그를 정확히 설정하고, PHP에서 동적으로 생성하고, XSS 방지와 캐싱을 고려하면 안정적인 공유 환경을 만들 수 있다. 이 글의 방법 2(동적 메타데이터 생성)를 참고해서 자신의 웹사이트에 맞게 상품 정보를 연동하면, 사용자들이 카톡으로 손쉽게 페이지를 공유할 수 있는 경험을 제공할 수 있을 것이다.