feat: 생년월일 필드 추가 + 카카오 스코프 확장 + 채널 연동 문서

- signup.html: 수집 목적 안내 카드, 생년월일(선택) 필드, 필수/선택 배지
- app.py: /api/signup에 birthday 처리, get_or_create_user birthday 파라미터
- mileage_schema.sql: users 테이블 birthday 컬럼 추가
- dbsetup.py: 기존 DB 마이그레이션 (ALTER TABLE ADD birthday)
- kakao_client.py: scope에 phone_number,birthday,birthyear 추가
- privacy.html: 항목별 수집 목적 테이블, 필수/선택 구분, 9항 신설
- kakao-phone-request.md: 전화번호+생일 스코프 신청 사유 문서
- kakao-channel-integration.md: 채널 API 분석 및 알림톡 로드맵
- kakao-chanell-rest-api.md: 카카오 채널 REST API 원문 참고 문서

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
thug0bin 2026-02-25 10:12:41 +09:00
parent 2b3d8649ba
commit f969756caa
9 changed files with 984 additions and 36 deletions

View File

@ -366,13 +366,14 @@ def verify_claim_token(transaction_id, nonce):
return (False, f"토큰 검증 실패: {str(e)}", None)
def get_or_create_user(phone, name):
def get_or_create_user(phone, name, birthday=None):
"""
사용자 조회 또는 생성 (간편 적립용)
Args:
phone (str): 전화번호
name (str): 이름
birthday (str, optional): 생년월일 (YYYY-MM-DD)
Returns:
tuple: (user_id, is_new_user)
@ -388,13 +389,17 @@ def get_or_create_user(phone, name):
user = cursor.fetchone()
if user:
# 기존 유저: birthday가 제공되면 업데이트
if birthday:
cursor.execute("UPDATE users SET birthday = ? WHERE id = ?", (birthday, user['id']))
conn.commit()
return (user['id'], False)
# 신규 생성
cursor.execute("""
INSERT INTO users (nickname, phone, mileage_balance)
VALUES (?, ?, 0)
""", (name, phone))
INSERT INTO users (nickname, phone, birthday, mileage_balance)
VALUES (?, ?, ?, 0)
""", (name, phone, birthday))
conn.commit()
return (cursor.lastrowid, True)
@ -550,6 +555,7 @@ def api_signup():
data = request.get_json()
name = data.get('name', '').strip()
phone = data.get('phone', '').strip().replace('-', '').replace(' ', '')
birthday = data.get('birthday', '').strip() or None # 선택 항목
if not name or not phone:
return jsonify({'success': False, 'message': '이름과 전화번호를 모두 입력해주세요.'}), 400
@ -557,7 +563,7 @@ def api_signup():
if len(phone) < 10:
return jsonify({'success': False, 'message': '올바른 전화번호를 입력해주세요.'}), 400
user_id, is_new = get_or_create_user(phone, name)
user_id, is_new = get_or_create_user(phone, name, birthday=birthday)
# 세션에 유저 정보 저장
session.permanent = True

View File

@ -213,6 +213,7 @@ class DatabaseManager:
print(f"[DB Manager] SQLite 신규 DB 생성 완료: {self.sqlite_db_path}")
else:
print(f"[DB Manager] SQLite 기존 DB 연결: {self.sqlite_db_path}")
self._migrate_sqlite()
return self.sqlite_conn
@ -235,6 +236,16 @@ class DatabaseManager:
print(f"[DB Manager] SQLite 스키마 초기화 완료")
def _migrate_sqlite(self):
"""기존 DB에 새 컬럼 추가 (마이그레이션)"""
cursor = self.sqlite_conn.cursor()
cursor.execute("PRAGMA table_info(users)")
columns = [row[1] for row in cursor.fetchall()]
if 'birthday' not in columns:
cursor.execute("ALTER TABLE users ADD COLUMN birthday VARCHAR(10)")
self.sqlite_conn.commit()
print("[DB Manager] SQLite 마이그레이션: users.birthday 컬럼 추가")
def test_connection(self, database='PM_BASE'):
"""연결 테스트"""
try:

View File

@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS users (
email VARCHAR(200),
is_email_verified BOOLEAN DEFAULT FALSE,
phone VARCHAR(20),
birthday VARCHAR(10),
mileage_balance INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP

View File

@ -39,7 +39,7 @@ class KakaoAPIClient:
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'response_type': 'code',
'scope': 'profile_nickname,profile_image,account_email,name'
'scope': 'profile_nickname,profile_image,account_email,name,phone_number,birthday,birthyear'
}
if state:
@ -137,6 +137,8 @@ class KakaoAPIClient:
'is_email_verified': kakao_account.get('is_email_verified', False),
'name': kakao_account.get('name'),
'phone_number': kakao_account.get('phone_number'),
'birthday': kakao_account.get('birthday'), # MMDD 형식
'birthyear': kakao_account.get('birthyear'), # YYYY 형식
}
# None 값 제거

View File

@ -128,6 +128,18 @@
line-height: 1.6;
}
.badge {
display: inline-block;
font-size: 11px;
font-weight: 600;
padding: 1px 5px;
border-radius: 3px;
vertical-align: middle;
}
.badge-req { background: #fff0f0; color: #e03131; }
.badge-opt { background: #f0f4ff; color: #6366f1; }
.effective-date {
color: #868e96;
font-size: 13px;
@ -160,28 +172,57 @@
<tr>
<th>수집 방법</th>
<th>수집 항목</th>
<th>필수/선택</th>
</tr>
<tr>
<td>직접 입력</td>
<td>직접 입력<br>(회원가입)</td>
<td>전화번호, 이름</td>
<td><span class="badge badge-req">필수</span></td>
</tr>
<tr>
<td>직접 입력<br>(회원가입)</td>
<td>생년월일</td>
<td><span class="badge badge-opt">선택</span></td>
</tr>
<tr>
<td>카카오 로그인</td>
<td>카카오 계정 식별자(ID), 닉네임, 프로필 이미지, 이메일, 이름</td>
<td>카카오 계정 식별자(ID), 닉네임, 프로필 이미지, 이메일, 이름, 전화번호, 생년월일</td>
<td><span class="badge badge-req">필수</span> / <span class="badge badge-opt">선택</span></td>
</tr>
<tr>
<td>자동 수집</td>
<td>구매 내역(품목명, 수량, 금액, 일시)</td>
<td><span class="badge badge-req">필수</span></td>
</tr>
</table>
<div class="section-title">2. 개인정보의 수집 및 이용 목적</div>
<ul>
<li>마일리지 포인트 적립 및 관리</li>
<li>고객 식별 및 본인 확인</li>
<li>적립 내역 조회 서비스 제공</li>
<li>구매 이력 기반 맞춤 서비스 제공</li>
</ul>
<table class="info-table">
<tr>
<th>수집 항목</th>
<th>이용 목적</th>
</tr>
<tr>
<td>전화번호</td>
<td>마일리지 적립 계정의 고유 식별자, 포인트 조회 시 본인 확인, 약국 방문 시 포인트 사용을 위한 본인 확인</td>
</tr>
<tr>
<td>이름</td>
<td>동명이인 구분 및 약국 방문 시 본인 확인, 적립 내역 안내</td>
</tr>
<tr>
<td>생년월일</td>
<td>생일 기념 포인트 2배 적립 이벤트, 연령대별 맞춤 건강 정보 및 제품 추천 서비스 제공</td>
</tr>
<tr>
<td>카카오 계정 정보</td>
<td>간편 로그인 및 자동 적립 기능 지원, 기존 회원과의 계정 연동</td>
</tr>
<tr>
<td>구매 내역</td>
<td>마일리지 포인트 적립 금액 산정, 적립 내역 조회 서비스 제공</td>
</tr>
</table>
<div class="section-title">3. 개인정보의 보유 및 이용 기간</div>
<p>약국은 개인정보 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. 단, 관계 법령에 의해 보존이 필요한 경우에는 해당 법령에서 정한 기간 동안 보관합니다.</p>
@ -241,6 +282,9 @@
<div class="section-title">8. 개인정보 자동 수집 장치의 설치·운영 및 거부</div>
<p>약국은 서비스 이용 과정에서 세션 쿠키를 사용하여 로그인 상태를 유지합니다. 쿠키는 브라우저 설정을 통해 거부할 수 있으나, 이 경우 자동 적립 기능 등 일부 서비스 이용이 제한될 수 있습니다.</p>
<div class="section-title">9. 선택 정보 미제공에 따른 불이익</div>
<p>생년월일 등 선택 항목을 제공하지 않더라도 마일리지 적립·조회 등 기본 서비스 이용에는 제한이 없습니다. 다만 생일 기념 포인트 이벤트, 연령대별 맞춤 추천 등 부가 서비스를 받으실 수 없습니다.</p>
<div class="effective-date">
시행일: 2026년 2월 25일
</div>

View File

@ -78,18 +78,47 @@
}
.input-group {
margin-bottom: 20px;
margin-bottom: 24px;
}
.input-group label {
display: block;
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
font-weight: 600;
color: #495057;
margin-bottom: 8px;
margin-bottom: 6px;
letter-spacing: -0.2px;
}
.label-badge {
font-size: 11px;
font-weight: 600;
padding: 2px 6px;
border-radius: 4px;
letter-spacing: -0.2px;
}
.badge-required {
background: #fff0f0;
color: #e03131;
}
.badge-optional {
background: #f0f4ff;
color: #6366f1;
}
.input-purpose {
font-size: 12px;
color: #868e96;
margin-bottom: 8px;
line-height: 1.5;
letter-spacing: -0.2px;
padding-left: 2px;
}
.input-group input {
width: 100%;
padding: 16px;
@ -129,6 +158,94 @@
flex: 1;
}
.birthday-wrapper {
display: flex;
gap: 8px;
}
.birthday-wrapper select {
flex: 1;
padding: 16px 12px;
border: 1.5px solid #e9ecef;
border-radius: 12px;
font-size: 15px;
font-family: inherit;
color: #212529;
background: #fff;
-webkit-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23868e96' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
}
.birthday-wrapper select:focus {
outline: none;
border-color: #6366f1;
}
.birthday-wrapper select.placeholder {
color: #adb5bd;
}
/* 수집 항목 안내 카드 */
.info-card {
background: #f8f9fa;
border-radius: 14px;
padding: 20px;
margin-bottom: 24px;
}
.info-card-title {
font-size: 13px;
font-weight: 700;
color: #495057;
margin-bottom: 12px;
letter-spacing: -0.2px;
}
.info-item {
display: flex;
align-items: flex-start;
gap: 10px;
margin-bottom: 10px;
}
.info-item:last-child { margin-bottom: 0; }
.info-item-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.info-item-icon.blue { background: rgba(99, 102, 241, 0.1); }
.info-item-icon.green { background: rgba(16, 185, 129, 0.1); }
.info-item-icon.pink { background: rgba(244, 63, 94, 0.1); }
.info-item-text {
flex: 1;
}
.info-item-label {
font-size: 13px;
font-weight: 600;
color: #495057;
letter-spacing: -0.2px;
}
.info-item-desc {
font-size: 12px;
color: #868e96;
line-height: 1.5;
letter-spacing: -0.2px;
}
.privacy-consent {
margin: 24px 0;
}
@ -346,17 +463,51 @@
<div class="form-title">회원가입</div>
<div class="form-desc">
청춘약국 마일리지 서비스에 가입하세요.<br>
영수증 QR 스캔으로 포인트를 적립할 수 있습니다.
영수증 QR 스캔으로 구매금액의 3%를 적립할 수 있습니다.
</div>
<!-- 수집 항목 안내 카드 -->
<div class="info-card">
<div class="info-card-title">수집 항목 및 이용 목적</div>
<div class="info-item">
<div class="info-item-icon blue">📱</div>
<div class="info-item-text">
<div class="info-item-label">전화번호 (필수)</div>
<div class="info-item-desc">마일리지 적립 계정의 고유 식별자로 사용됩니다. 포인트 조회 및 사용 시 본인 확인에 필요합니다.</div>
</div>
</div>
<div class="info-item">
<div class="info-item-icon green">👤</div>
<div class="info-item-text">
<div class="info-item-label">이름 (필수)</div>
<div class="info-item-desc">동명이인 구분 및 약국 방문 시 본인 확인에 사용됩니다.</div>
</div>
</div>
<div class="info-item">
<div class="info-item-icon pink">🎂</div>
<div class="info-item-text">
<div class="info-item-label">생년월일 (선택)</div>
<div class="info-item-desc">생일 기념 포인트 2배 적립 이벤트 및 연령대별 맞춤 건강 정보 제공에 활용됩니다.</div>
</div>
</div>
</div>
<form id="formSignup" onsubmit="return false;">
<div class="input-group">
<label for="name">이름</label>
<label for="name">
이름
<span class="label-badge badge-required">필수</span>
</label>
<div class="input-purpose">약국 방문 시 본인 확인 및 동명이인 구분에 사용됩니다.</div>
<input type="text" id="name" placeholder="이름을 입력하세요" autocomplete="name" required>
</div>
<div class="input-group">
<label for="phone">전화번호</label>
<label for="phone">
전화번호
<span class="label-badge badge-required">필수</span>
</label>
<div class="input-purpose">마일리지 적립·조회의 고유 식별자로 사용됩니다.</div>
<div class="phone-wrapper">
<span class="phone-prefix">010 -</span>
<input type="tel" id="phone"
@ -368,12 +519,31 @@
</div>
</div>
<div class="input-group">
<label>
생년월일
<span class="label-badge badge-optional">선택</span>
</label>
<div class="input-purpose">생일 기념 포인트 2배 적립 및 연령대별 맞춤 건강 정보 제공에 활용됩니다.</div>
<div class="birthday-wrapper">
<select id="birthYear" class="placeholder">
<option value="">년도</option>
</select>
<select id="birthMonth" class="placeholder">
<option value=""></option>
</select>
<select id="birthDay" class="placeholder">
<option value=""></option>
</select>
</div>
</div>
<div class="privacy-consent">
<label class="checkbox-container">
<input type="checkbox" id="privacyConsent" required>
<span class="consent-text">
<a href="/privacy" target="_blank">개인정보 수집·이용</a>에 동의합니다.
<br><span style="font-size:12px; color:#868e96;">전화번호, 이름을 마일리지 적립·조회 목적으로 수집합니다.</span>
<br><span style="font-size:12px; color:#868e96;">전화번호, 이름(필수), 생년월일(선택)을 마일리지 적립·조회 및 맞춤 서비스 목적으로 수집합니다.</span>
</span>
</label>
</div>
@ -418,6 +588,41 @@
<script>
if('serviceWorker' in navigator){navigator.serviceWorker.register('/sw.js').catch(()=>{});}
// 생년월일 셀렉트 초기화
(function() {
var yearSel = document.getElementById('birthYear');
var monthSel = document.getElementById('birthMonth');
var daySel = document.getElementById('birthDay');
var currentYear = new Date().getFullYear();
for (var y = currentYear; y >= 1920; y--) {
var opt = document.createElement('option');
opt.value = y;
opt.textContent = y + '년';
yearSel.appendChild(opt);
}
for (var m = 1; m <= 12; m++) {
var opt = document.createElement('option');
opt.value = m < 10 ? '0' + m : '' + m;
opt.textContent = m + '월';
monthSel.appendChild(opt);
}
for (var d = 1; d <= 31; d++) {
var opt = document.createElement('option');
opt.value = d < 10 ? '0' + d : '' + d;
opt.textContent = d + '일';
daySel.appendChild(opt);
}
// 선택 시 placeholder 클래스 제거
[yearSel, monthSel, daySel].forEach(function(sel) {
sel.addEventListener('change', function() {
if (this.value) this.classList.remove('placeholder');
else this.classList.add('placeholder');
});
});
})();
const phoneInput = document.getElementById('phone');
const form = document.getElementById('formSignup');
const alertMsg = document.getElementById('alertMsg');
@ -437,6 +642,15 @@
const phone = '010' + raw;
const consent = document.getElementById('privacyConsent').checked;
// 생년월일 (선택)
const birthYear = document.getElementById('birthYear').value;
const birthMonth = document.getElementById('birthMonth').value;
const birthDay = document.getElementById('birthDay').value;
let birthday = null;
if (birthYear && birthMonth && birthDay) {
birthday = birthYear + '-' + birthMonth + '-' + birthDay;
}
if (!name) return showAlert('이름을 입력해주세요.');
if (raw.length < 7) return showAlert('올바른 전화번호를 입력해주세요.');
if (!consent) return showAlert('개인정보 수집·이용에 동의해주세요.');
@ -446,10 +660,13 @@
btn.textContent = '가입 중...';
try {
const body = { name, phone };
if (birthday) body.birthday = birthday;
const res = await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, phone })
body: JSON.stringify(body)
});
const data = await res.json();

View File

@ -0,0 +1,456 @@
REST API
이 문서는 REST API를 이용하여 카카오톡 채널 관계 조회 및 카카오톡 채널 고객 관리 기능을 구현하는 방법을 안내합니다.
카카오톡 채널 관계 조회
기본 정보
메서드 URL 인증 방식
GET https://kapi.kakao.com/v2/api/talk/channels 액세스 토큰
서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
필요: 동의항목 어드민 키
카카오 로그인 활성화
동의항목
앱에 카카오톡 채널 연결 필요 필요:
카카오톡 채널 추가 상태 및 내역
신규 API 제공 안내
카카오톡 채널 관계 조회 API가 v2 버전으로 업그레이드되었습니다. 기존 API 정보는 별도 문서에서 확인할 수 있습니다.
참고
사용자가 서비스와 연결된 카카오톡 채널을 추가 또는 차단했을 때 알림을 받으려면 카카오톡 채널 웹훅을 사용합니다.
현재 로그인한 사용자와 앱에 연결된 카카오톡 채널의 친구 관계를 확인합니다.
사용자 액세스 토큰(Access Token)을 헤더에 담아 GET으로 요청합니다. 서비스 서버에서 관리자가 요청할 경우, 앱별 어드민 키(Admin Key)로 특정 사용자의 카카오톡 채널 관계를 확인할 수 있습니다. 어드민 키는 보안에 유의해야 하므로 서버에서 호출할 때만 사용해야 합니다.
특정 카카오톡 채널의 정보만 받아보려면 channel_ids 파라미터로 해당 카카오톡 채널의 프로필 ID를 지정하여 요청합니다.
요청 성공 시 응답은 서비스 앱과 연결된 카카오톡 채널과 사용자의 관계 정보를 제공합니다. 각 카카오톡 채널 정보는 사용자와 카카오톡 채널의 현재 관계, 변경 시점과 같은 자세한 정보를 포함합니다.
사용자가 [카카오톡 채널 추가 상태 및 내역] 동의항목에 동의하지 않아 에러 응답을 받았을 경우, 동의항목 추가 동의 요청 기능을 사용해 사용자에게 다시 동의를 요청할 수 있습니다.
요청: 액세스 토큰 방식
헤더
이름 설명 필수
Authorization Authorization: Bearer ${ACCESS_TOKEN}
인증 방식, 액세스 토큰으로 인증 요청 O
쿼리 파라미터
이름 타입 설명 필수
channel_ids String 사용자와의 친구 관계를 확인할 카카오톡 채널 프로필 ID 목록
쉼표로 구분된 하나의 문자열로 전달
(예: _Bxkd,_RQxl,_vxfxm, 기본값: 앱과 연결된 모든 카카오톡 채널의 프로필 ID 목록)
참고: 카카오톡 채널 프로필 ID 확인 방법 X
channel_id_type String 카카오톡 채널 ID 타입, channel_public_id로 고정 X
요청: 서비스 앱 어드민 키 방식
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${SERVICE_APP_ADMIN_KEY}
인증 방식, 서비스 앱 어드민 키로 인증 요청 O
Content-Type Content-Type: application/x-www-form-urlencoded;charset=utf-8
요청 데이터 타입 O
쿼리 파라미터
이름 타입 설명 필수
target_id String 회원번호 O
target_id_type String 사용자 ID 타입, user_id로 고정 O
channel_ids String 사용자와의 친구 관계를 확인할 카카오톡 채널 프로필 ID 목록
쉼표로 구분된 하나의 문자열로 전달
(예: _Bxkd,_RQxl,_vxfxm, 기본값: 앱과 연결된 모든 카카오톡 채널의 프로필 ID 목록)
참고: 카카오톡 채널 프로필 ID 확인 방법 X
channel_id_type String 카카오톡 채널 ID 타입, channel_public_id로 고정 X
응답
본문
이름 타입 설명 필수
user_id Long 회원번호 O
channels Channels[] 카카오톡 채널 정보 X
Channels
이름 타입 설명 필수
channel_uuid String 카카오톡 채널의 검색용 ID O
channel_public_id String 카카오톡 채널 프로필 ID O
relation String 카카오톡 채널과 사용자 관계
ADDED: 카카오톡 채널이 추가된 상태
BLOCKED: 카카오톡 채널이 차단된 상태
NONE: 카카오톡 채널이 추가되거나 차단된 적 없는 상태 O
created_at Datetime 카카오톡 채널 추가 시간, UTC*
카카오톡 채널이 추가(ADDED) 상태인 경우만 포함 X
updated_at Datetime 카카오톡 채널 상태 변경 시간, UTC*
카카오톡 채널이 추가(ADDED) 또는 차단(BLOCKED)된 상태일 경우만 포함 X
* UTC: 한국 시간(KST)과 9시간 차이, RFC3339: Date and Time on the Internet 참고
예제
요청: 액세스 토큰 방식
curl -v -G GET "https://kapi.kakao.com/v2/api/talk/channels" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-d "channel_ids=_frxjem,_xnrxjem,_Brxjem"
요청: 서비스 앱 어드민 키 방식
curl -v -G GET "https://kapi.kakao.com/v2/api/talk/channels" \
-H "Authorization: KakaoAK ${SERVICE_APP_ADMIN_KEY}" \
-d "target_id_type=user_id" \
-d "target_id=${USER_ID}" \
-d "channel_ids=_frxjem,_xnrxjem,_Brxjem"
응답: 성공
HTTP/1.1 200 OK
{
"user_id": ${USER_ID},
"channels": [
{
"channel_uuid": "@테스트",
"channel_public_id": "_ZeUTxl",
"relation": "ADDED", // ADDED, BLOCKED, NONE 중 하나
"created_at": "2020-04-18T03:17:05Z", // ADDED 상태일 때만 존재
"updated_at": "2021-05-17T05:25:01Z" // ADDED, BLOCKED 상태일 때만 존재
},
...
]
}
응답: 실패, 카카오톡 미사용자를 대상으로 요청한 경우
HTTP/1.1 400 Bad Request
{
"msg": "given account is not connected to any talk user.",
"code": -501
}
여러 사용자 카카오톡 채널 관계 조회
기본 정보
메서드 URL 인증 방식
GET https://kapi.kakao.com/v2/api/talk/channels/multi 서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
필요: 동의항목 어드민 키
카카오 로그인 활성화
동의항목
앱에 카카오톡 채널 연결 필요 필요:
카카오톡 채널 추가 상태 및 내역
참고
사용자가 서비스와 연결된 카카오톡 채널을 추가 또는 차단했을 때 알림을 받으려면 카카오톡 채널 웹훅을 사용합니다.
앱에 연결된 카카오톡 채널과 여러 사용자의 친구 관계를 확인합니다. 전체 또는 그룹 단위의 사용자를 대상으로 특정 카카오톡 채널과의 친구 관계를 확인하는 데 사용합니다.
서비스 앱 어드민 키를 헤더에 담아 GET으로 요청합니다. 사용자 회원번호 목록, 카카오톡 채널 프로필 ID 목록을 쿼리 파라미터로 전달해야 합니다. 한 번에 최대 200명의 사용자를 대상으로 요청 가능합니다.
요청 처리 성공 시 응답은 각 사용자의 카카오톡 채널별 친구 관계 목록을 포함합니다. 확인에 실패한 사용자의 정보는 응답에서 제외됩니다.
요청
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${SERVICE_APP_ADMIN_KEY}
인증 방식, 서비스 앱 어드민 키로 인증 요청 O
Content-Type Content-Type: application/x-www-form-urlencoded;charset=utf-8
요청 데이터 타입 O
쿼리 파라미터
이름 타입 설명 필수
target_ids String 회원번호 목록, 쉼표로 구분된 하나의 문자열로 구성 O
target_id_type String 사용자 ID 타입, user_id로 고정 O
channel_ids String[] 사용자와의 친구 관계를 확인할 카카오톡 채널의 프로필 ID 목록, 쉼표로 구분된 하나의 문자열로 구성
(예: _Bxkd,_RQxl,_vxfxm, 기본값: 앱과 연결된 모든 카카오톡 채널의 프로필 ID 목록)
참고: 카카오톡 채널 프로필 ID 확인 방법 X
channel_id_type String 카카오톡 채널 ID 타입, channel_public_id로 고정 X
응답
본문
이름 타입 설명 필수
- TalkChannelsResult[] 각 사용자의 카카오톡 채널별 친구 관계 목록 O
TalkChannelsResult
이름 타입 설명 필수
user_id Long 회원번호 O
channels TalkChannelRelation[] 각 카카오톡 채널과 사용자의 관계 정보 X
TalkChannelRelation
이름 타입 설명 필수
channel_public_id String 카카오톡 채널 프로필 ID O
channel_uuid String 카카오톡 채널의 검색용 ID O
relation String 카카오톡 채널과 사용자 관계
ADDED: 카카오톡 채널이 추가된 상태
BLOCKED: 카카오톡 채널이 차단된 상태
NONE: 카카오톡 채널이 추가되거나 차단된 적 없는 상태 O
created_at Datetime 카카오톡 채널 추가 시간, UTC*
카카오톡 채널이 추가(ADDED) 상태인 경우만 포함 X
updated_at Datetime 카카오톡 채널 상태 변경 시간, UTC*
카카오톡 채널이 추가(ADDED) 또는 차단(BLOCKED)된 상태일 경우만 포함 X
* UTC: 한국 시간(KST)과 9시간 차이, RFC3339: Date and Time on the Internet 참고
예제
요청
curl -v -G GET "https://kapi.kakao.com/v2/api/talk/channels/multi" \
-H "Authorization: KakaoAK ${SERVICE_APP_ADMIN_KEY}" \
-d "target_id_type=user_id" \
-d "target_ids=${USER_ID_1},${USER_ID_2},${USER_ID_3}" \
--data-urlencode 'channel_ids=_frxjem,_xnrxjem,_Brxjem'
응답
HTTP/1.1 200 OK
[
{
"user_id": ${USER_ID_1},
"channels": [
{
"channel_public_id": "_xnrxjem",
"channel_uuid": "@플러스친구",
"relation": "ADDED",
"created_at": "2022-11-09T07:08:48Z",
"updated_at": "2023-07-20T07:21:05Z"
}
]
}, {
"user_id": ${USER_ID_2},
"channels": [
{
"channel_public_id": "_xnrxjem",
"channel_uuid": "@플러스친구",
"relation": "NONE"
}
]
},
...
]
응답: 확인에 실패한 사용자 제외
HTTP/1.1 200 OK
[
{
"user_id": ${USER_ID_1},
"channels": [
{
"channel_public_id": "_xnrxjem",
"channel_uuid": "@플러스친구",
"relation": "ADDED",
"created_at": "2022-11-09T07:08:48Z",
"updated_at": "2023-07-20T07:21:05Z"
}
]
}
]
고객 관리: 고객파일 등록
기본 정보
메서드 URL 인증 방식
POST https://kapi.kakao.com/v1/talkchannel/create/target_user_file REST API 키
서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
- REST API 키
어드민 키
고객 관리 API 정책 동의 - -
새로운 고객파일을 만듭니다. 새 파일 이름은 file_name, 적용할 필터링 기준은 schema에 각각 정의합니다. 한 번 정의한 스키마(Schema)는 수정할 수 없으니 주의합니다.
새 고객파일을 만드는 데 성공하면 file_id 값으로 등록된 파일 ID가 반환됩니다. 파일 ID는 해당 파일에 사용자를 추가하거나 제외할 때 사용합니다.
제약 사항: 스키마
카카오톡 채널 고객 관리 API를 이용하여 고객파일을 등록할 경우, 반드시 지정된 스키마 규칙을 따라야 합니다.
고객의 데이터가 문자열(String)인 경우, 지원하는 키만 사용 가능
생년월일, 국가, 지역, 성별, 연령, 구매금액, 포인트, 가입일, 최근 구매일, 응모일
새로운 키 추가 시 "앱유저아이디" 또는 "전화번호" 키 사용 불가, 키에 해당하는 값은 숫자(Number) 자료형만 허용
스키마는 최대 30개 항목 포함 가능
요청
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${APP_KEY}
인증 방식, REST API 키 또는 서비스 앱 어드민 키로 인증 요청 O
본문
이름 타입 설명 필수
channel_public_id String 카카오톡 채널 프로필 ID
참고: 카카오톡 채널 프로필 ID 확인 방법 O
schema JSON 고객파일에 등록되는 데이터 항목과 항목의 종류를 정의
키(Key)와 값(Value)의 JSON 자료형(Type)으로 구성
키: 생년월일, 국가, 지역, 성별, 연령, 구매금액, 포인트, 가입일, 최근 구매일, 응모일
값의 자료형: String 또는 Number O
file_name String 관리할 파일의 이름 O
응답
본문
이름 타입 설명
file_id Integer 등록된 고객파일 ID
예제
요청
curl -v -X POST "https://kapi.kakao.com/v1/talkchannel/create/target_user_file" \
-H "Authorization: KakaoAK ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"channel_public_id": "_ZeUTxl",
"file_name": "vip고객리스트",
"schema":{
"생년월일":"string",
"성별":"string",
"연령":"number"
}
}'
응답
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"file_id" : 437
}
고객 관리: 고객파일 조회
기본 정보
메서드 URL 인증 방식
GET https://kapi.kakao.com/v1/talkchannel/target_user_file REST API 키
서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
- REST API 키
어드민 키
고객 관리 API 정책 동의 - -
카카오톡 채널에 등록된 고객파일 정보들을 확인합니다. 어떤 카카오톡 채널에 등록된 파일 정보들을 알고 싶은지 channel_public_id 파라미터의 값을 명시하여 요청합니다. 요청 성공 시 해당 카카오톡 채널에 등록된 고객파일들의 정보를 받습니다.
요청
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${APP_KEY}
인증 방식, REST API 키 또는 서비스 앱 어드민 키로 인증 요청 O
쿼리 파라미터
이름 타입 설명 필수
channel_public_id String 카카오톡 채널 프로필 ID
참고: 카카오톡 채널 프로필 ID 확인 방법 O
응답
본문
이름 타입 설명
empty_slot Integer 사용 가능한 슬롯 수
using_slot Integer 사용 중인 슬롯 수
results Results[] 카카오톡 채널에 등록된 고객파일들의 정보
Results
이름 타입 설명
file_id Integer 파일 ID
file_name String 파일 이름
status String 파일 상태
using, deleting, failed 중 하나
update_at String 파일이 업로드 된 시간
schema JSON 파일에 등록된 데이터 항목과 항목의 종류
예제
요청
curl -v -G GET "https://kapi.kakao.com/v1/talkchannel/target_user_file" \
-H "Authorization: KakaoAK ${APP_KEY}" \
-d "channel_public_id=_ZeUTxl"
응답
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"empty_slot":27,
"using_slot":3,
"results":[
{
"file_id":437,
"file_name": "vip고객리스트",
"status":"USING",
"update_at":"2019-02-03 13:22:33",
"schema": "{\"생년월일\":\"string\",\"성별\":\"string\",\"age\":\"number\"}"
},
...
]
}
고객 관리: 고객파일에 사용자 추가
기본 정보
메서드 URL 인증 방식
POST https://kapi.kakao.com/v1/talkchannel/update/target_users REST API 키
서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
- REST API 키
어드민 키
고객 관리 API 정책 동의 - -
고객파일에 사용자 정보를 추가합니다. 한 번에 2,000명 이하의 고객 정보를 업로드할 수 있습니다. 각 사용자를 구분하는 값인 id는 회원번호(user_id)와 카카오톡 전화번호 중 하나여야 하고, 지정된 타입에 맞는 값을 입력해야 합니다. 스키마의 경우, 파일마다 다르게 지정되어 있으나 예제를 참고해 JSON 배열 형식으로 값을 전달합니다.
요청 성공 시, 어떤 고객파일에 대한 요청이었는지 알려주는 file_id와 고객파일에 추가 요청한 사용자 수, 실제로 추가된 사용자 수를 각각 받습니다. 추가 대상 사용자 정보가 유효하지 않거나 아래의 경우에는 고객파일에 사용자가 추가되지 않습니다. 따라서 추가 요청 사용자 수와 실제로 추가된 사용자 수는 차이가 날 수 있습니다.
참고: 고객파일에 일부 사용자가 추가되지 않은 경우 확인 항목
아래 내용을 확인합니다.
카카오톡 채널과 친구 상태인 사용자만 고객파일에 추가 가능합니다.
user_type이 app인 경우, ID 값이 카카오 로그인으로 발급된 회원번호(user id)여야 합니다. 즉, 해당 사용자가 카카오계정으로 서비스에 연결된 상태여야 합니다.
user_type이 phone인 경우, ID 값이 카카오톡에 가입되어 있는 전화번호여야 합니다.
요청
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${APP_KEY}
인증 방식, REST API 키 또는 서비스 앱 어드민 키로 인증 요청 O
본문
이름 타입 설명 필수
file_id Integer 파일 ID O
channel_public_id String 카카오톡 채널 프로필 ID
참고: 카카오톡 채널 프로필 ID 확인 방법 O
user_type String 등록할 사용자 ID의 기준 값
app(회원번호) 또는 phone(카카오톡 전화번호) 중 하나 O
users User[] 추가할 사용자 상세 정보 목록, ID와 스키마 값 포함 O
User
이름 타입 설명 필수
id String 사용자 ID O
field JSON 지정된 스키마 대한 값
key, value 형태로 입력
참고: Number 또는 String 타입만 허용, String 타입인 경우 지정된 문자열만 사용 가능, 문자열은 카카오톡 채널 파트너센터 공지에 명시된 항목만 지정된 형식으로 변환해 입력 가능 O
응답
본문
이름 타입 설명
file_id Integer 파일 ID
request_count Integer 고객파일에 추가 요청한 사용자 수
success_count Integer 고객파일에 추가된 사용자 수
예제
요청
curl -v -X POST "https://kapi.kakao.com/v1/talkchannel/update/target_users" \
-H "Authorization: KakaoAK ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"file_id": 437,
"channel_public_id": "_ZeUTxl",
"user_type": "app",
"users": [
{
"id": "12345",
"field" : {
"생년월일": "2000-01-01",
"성별": "남자",
"age": 19
}
},
...
]
}'
응답
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"file_id": 437,
"request_count": 10,
"success_count": 9
}
고객 관리: 고객파일의 사용자 삭제
기본 정보
메서드 URL 인증 방식
POST https://kapi.kakao.com/v1/talkchannel/delete/target_users REST API 키
서비스 앱 어드민 키
권한 사전 설정 카카오 로그인 동의항목
- REST API 키
어드민 키
고객 관리 API 정책 동의 - -
카카오톡 채널에 등록된 고객파일에서 특정 사용자를 삭제합니다. 고객파일에서 사용자를 삭제할 때는 성공 시에도 응답 본문이 없습니다. HTTP 상태 코드를 참고해 성공 여부를 판단합니다.
요청
헤더
이름 설명 필수
Authorization Authorization: KakaoAK ${APP_KEY}
인증 방식, REST API 키 또는 서비스 앱 어드민 키로 인증 요청 O
본문
이름 타입 설명 필수
file_id Integer 파일 ID O
channel_public_id String 카카오톡 채널 프로필 ID
참고: 카카오톡 채널 프로필 ID 확인 방법 O
user_type String 삭제할 사용자 ID의 기준 값
app(회원번호) 또는 phone(카카오톡 전화번호) 중 하나 O
user_ids JSON[] 삭제할 사용자 ID 목록 O
예제
요청
curl -v -X POST "https://kapi.kakao.com/v1/talkchannel/delete/target_users" \
-H "Authorization: KakaoAK ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"file_id" : 437,
"channel_public_id" : "_ZeUTxl",
"user_type" : "app"
"user_ids" : ["12345"]
}'
응답
HTTP/1.1 200 OK
Content-Length: 0
Content-Type: application/json;charset=UTF-8
더 보기

View File

@ -0,0 +1,146 @@
# 카카오톡 채널 연동 분석
## 현재 상태
- 카카오톡 채널 개설 완료
- 앱과 채널 연결 완료
---
## API 종류 및 우리 서비스 적용 가능성
### 1. 채널 관계 조회 API
- **기능**: 사용자가 우리 채널을 친구 추가했는지 확인
- **우선순위**: 낮음 (나중에)
- **활용**: 적립 완료 시 채널 친구가 아니면 "채널 추가하면 생일 2배 적립 알림을 받을 수 있어요!" 유도 배너
### 2. 고객파일 관리 API
- **기능**: 채널 친구인 고객의 데이터를 카카오에 업로드 → 파트너센터에서 세그먼트 필터링
- **우선순위**: 낮음 (고객 수백 명 이상 쌓인 후)
- **활용**: 파트너센터에서 "포인트 5000 이상 고객"에게 친구톡 발송 등
#### 고객파일 스키마 (사용 가능한 키)
```
생년월일, 국가, 지역, 성별, 연령, 구매금액, 포인트, 가입일, 최근 구매일, 응모일
```
- 이 값들은 **카카오가 제공하는 것이 아님**
- **우리 DB에서 꺼내서 카카오에 업로드**하는 것
- 고객 본인이 카카오톡에서 보는 게 아니라, **약국(관리자)이 파트너센터에서 고객 분류/메시지 발송할 때 사용**하는 필터링 기준
---
## 메시지 발송 수단 비교
### 알림톡 (정보성 메시지)
| 항목 | 내용 |
|------|------|
| 대상 | 전화번호만 있으면 **누구에게나** (채널 친구 불필요) |
| 템플릿 | 카카오 사전 심사 필수 (정형화된 형식) |
| 용도 | 정보성 메시지 (적립 완료 알림, 주문 확인 등) |
| 비용 | ~8원/건 |
| 발송 방법 | NHN Cloud 알림톡 API |
### 친구톡 (마케팅 메시지)
| 항목 | 내용 |
|------|------|
| 대상 | **채널 친구에게만** |
| 템플릿 | 자유 형식 (이미지, 버튼 등 자유롭게 구성) |
| 용도 | 광고/마케팅 메시지 (생일 이벤트, 프로모션 등) |
| 비용 | ~15원/건 |
| 발송 방법 | NHN Cloud 친구톡 API 또는 카카오 파트너센터에서 직접 발송 |
### SMS/LMS (문자)
| 항목 | 내용 |
|------|------|
| 대상 | 전화번호만 있으면 누구에게나 |
| 템플릿 | 제한 없음 |
| 용도 | 범용 |
| 비용 | SMS ~20원, LMS ~50원 |
| 발송 방법 | NHN Cloud SMS API |
### 카카오 파트너센터 직접 발송
| 항목 | 내용 |
|------|------|
| 대상 | 채널 친구만 |
| 방법 | 파트너센터 웹에서 수동 발송 |
| 활용 | 고객파일 세그먼트 기반 타겟 메시지 |
| 특징 | API 개발 불필요, UI에서 직접 조작 |
---
## 우리 서비스에 적용할 알림톡 시나리오
### 시나리오 1: QR 적립 완료 알림 (현재 불필요 → 키오스크 도입 시 필요)
현재는 고객이 직접 QR 스캔 → 적립 완료 화면을 본인이 확인하므로 알림 불필요.
**키오스크 도입 후**: 약사가 키오스크에서 직접 적립 → 고객은 화면을 못 봄 → 알림톡 필요
```
[청춘약국] 마일리지 적립 완료
{고객명}님, 마일리지가 적립되었습니다.
- 적립 포인트: +3,500P
- 총 잔액: 12,800P
- 적립일시: 2026.02.25 14:30
▶ 내역 확인: https://mile.0bin.in/my-page
```
### 시나리오 2: 포인트 사용 알림
```
[청춘약국] 포인트 사용 완료
{고객명}님, 포인트가 사용되었습니다.
- 사용 포인트: -5,000P
- 남은 잔액: 7,800P
▶ 내역 확인: https://mile.0bin.in/my-page
```
### 시나리오 3: 생일 축하 (친구톡 — 채널 친구만)
```
🎂 {고객명}님, 생일 축하드립니다!
오늘 청춘약국에서 구매하시면
마일리지 포인트 2배 적립!
▶ 청춘약국 방문하기
```
---
## 구현 로드맵
### Phase 1 (현재)
- [x] 카카오 채널 개설 및 앱 연결
- [ ] 카카오 스코프 심사 통과 (phone_number, birthday, birthyear)
### Phase 2 (키오스크 도입 시)
- [ ] NHN Cloud 알림톡 API 연동
- [ ] 알림톡 템플릿 등록 (적립 완료, 포인트 사용)
- [ ] 키오스크 적립 시 알림톡 자동 발송
### Phase 3 (고객 확보 후)
- [ ] 채널 친구 추가 유도 (적립 완료 화면에 배너)
- [ ] 생일 축하 친구톡 발송 (birthday 데이터 활용)
- [ ] (선택) 카카오 고객파일 동기화 → 파트너센터 세그먼트 마케팅
---
## 필요한 환경변수 (Phase 2 시점)
```env
# NHN Cloud 알림톡
NHN_CLOUD_APP_KEY=xxx
NHN_CLOUD_SECRET_KEY=xxx
NHN_ALIMTALK_SENDER_KEY=xxx # 카카오 채널 발신 프로필 키
NHN_ALIMTALK_TEMPLATE_CODE=xxx # 적립 완료 템플릿 코드
```
## 참고
- 알림톡 템플릿은 카카오 비즈니스 채널 관리자에서 등록 후 검수 받아야 함 (1~2일 소요)
- NHN Cloud 알림톡 발송 시 카카오톡 미설치 사용자에게는 자동으로 SMS 대체 발송 가능 (추가 비용)

View File

@ -1,6 +1,20 @@
# 카카오 개인정보 동의항목 - 전화번호 수집 신청
# 카카오 개인정보 동의항목 - 추가 스코프 신청
## 수집 사유 (신청 폼에 입력할 내용)
## 요청 스코프 목록
| 스코프 | 항목 | 필수/선택 | 현재 상태 |
|--------|------|-----------|-----------|
| `profile_nickname` | 닉네임 | 필수 | 승인됨 |
| `profile_image` | 프로필 이미지 | 필수 | 승인됨 |
| `account_email` | 이메일 | 선택 | 승인됨 |
| `name` | 이름 | 필수 | 승인됨 |
| `phone_number` | 전화번호 | 필수 | **신청 필요** |
| `birthday` | 생일 (월/일) | 선택 | **신청 필요** |
| `birthyear` | 출생연도 | 선택 | **신청 필요** |
---
## 1. 전화번호 (phone_number) 수집 사유
```
청춘약국 마일리지 적립 서비스에서 고객 식별을 위해 전화번호가 필요합니다.
@ -16,6 +30,8 @@
전화번호로 조회합니다.
3. 오프라인 연계: 약국 방문 시 포인트 사용을 위해 전화번호로
본인 확인이 필요합니다.
4. 동명이인 구분: 이름만으로는 동일인 여부를 확인할 수 없어,
전화번호가 필수적인 고유 식별 수단입니다.
[현재 상황]
전화번호를 카카오에서 받지 못하는 경우, 카카오 로그인 후
@ -30,22 +46,60 @@
- 제3자 제공: 없음
```
## 2. 생일/출생연도 (birthday, birthyear) 수집 사유
```
청춘약국 마일리지 서비스에서 생년월일 정보를 선택적으로 수집하여
맞춤형 서비스를 제공하고자 합니다.
[생년월일이 필요한 이유]
1. 생일 기념 이벤트: 회원 생일에 마일리지 포인트 2배 적립 이벤트를
제공합니다. 생일 당일 구매 시 기본 적립률(3%) 대신 6%를 적립합니다.
2. 연령대별 맞춤 건강 정보: 구매 이력과 연령대를 결합하여
맞춤 건강 정보 및 추천 제품을 안내합니다.
(예: 50대 이상 → 관절/영양 보충제 정보, 20~30대 → 피부/다이어트 관련)
3. 서비스 통계: 연령대별 이용 현황 분석으로 서비스 품질을 개선합니다.
[선택 항목]
생년월일은 선택 수집 항목이며, 미제공 시에도 마일리지 적립·조회 등
기본 서비스 이용에는 제한이 없습니다.
[수집 범위]
- 수집 항목: 생일(월/일), 출생연도
- 이용 목적: 생일 이벤트, 연령대별 맞춤 서비스
- 보유 기간: 회원 탈퇴 시까지
- 제3자 제공: 없음
```
---
## 회원가입 시나리오 (회원가입 화면 설명용)
### 시나리오 1: 카카오 로그인으로 QR 적립 (메인 플로우)
### 시나리오 1: 직접 회원가입 (수동)
```
1. https://mile.0bin.in 접속
2. "회원가입" 메뉴 클릭 → https://mile.0bin.in/signup 이동
3. 이름(필수), 전화번호(필수), 생년월일(선택) 입력
4. 개인정보 수집·이용 동의 체크
5. "가입하기" 버튼 클릭
6. 가입 완료 → 마이페이지 이동 가능
```
### 시나리오 2: 카카오 로그인으로 QR 적립 (메인 플로우)
```
1. 고객이 약국에서 의약품 구매 후 영수증을 받음
2. 영수증에 인쇄된 QR 코드를 스마트폰 카메라로 스캔
3. https://mile.0bin.in/claim?t=거래번호:인증코드 페이지 이동
4. "카카오로 적립하기" 버튼 클릭
5. 카카오 로그인 동의 화면 표시 (닉네임, 프로필, 이메일, 이름, 전화번호)
5. 카카오 로그인 동의 화면 표시 (닉네임, 프로필, 이메일, 이름, 전화번호, 생년월일)
6-A. [전화번호 수집 가능 시] → 자동으로 마일리지 적립 완료
6-B. [전화번호 미수집 시] → 전화번호 입력 화면으로 이동 → 수동 입력 후 적립
7. 적립 완료 화면 표시 (적립 포인트, 총 잔액)
```
### 시나리오 2: 카카오 로그인으로 마이페이지 조회
### 시나리오 3: 카카오 로그인으로 마이페이지 조회
```
1. https://mile.0bin.in 접속
@ -54,7 +108,7 @@
4. 마이페이지 이동 (적립 내역, 포인트 잔액 확인)
```
### 시나리오 3: PWA 앱에서 자동 적립 (재방문 고객)
### 시나리오 4: PWA 앱에서 자동 적립 (재방문 고객)
```
1. 이전에 카카오 로그인으로 적립한 이력이 있는 고객
@ -62,18 +116,29 @@
3. 세션이 유지되어 있으므로 입력 없이 자동 적립 완료
```
---
## 제출 체크리스트
- [x] 회원가입 링크: `https://mile.0bin.in`
### 전화번호 (phone_number) 신청
- [x] 회원가입 링크: `https://mile.0bin.in/signup`
- [x] 개인정보 처리방침: `https://mile.0bin.in/privacy`
- [ ] 회원가입 화면 스크린샷: 카카오 로그인 버튼이 보이는 적립 화면 캡처
- `/claim?t=거래번호:인증코드` 페이지 또는 메인 페이지 캡처
- 개인정보가 보이면 마스킹 필요
- [ ] 수집 사유: 위 텍스트 복사하여 입력
- [ ] 회원가입 화면 스크린샷: `/signup` 페이지 캡처 (전화번호 필드 + 수집 목적 안내 + 개인정보 동의 포함)
- [ ] 수집 사유: 위 "1. 전화번호 수집 사유" 텍스트 복사하여 입력
### 생일/출생연도 (birthday, birthyear) 신청
- [x] 회원가입 링크: `https://mile.0bin.in/signup`
- [x] 개인정보 처리방침: `https://mile.0bin.in/privacy`
- [ ] 회원가입 화면 스크린샷: `/signup` 페이지 캡처 (생년월일 필드 + "선택" 배지 + 수집 목적 안내 포함)
- [ ] 수집 사유: 위 "2. 생일/출생연도 수집 사유" 텍스트 복사하여 입력
## 스크린샷 촬영 가이드
회원가입 화면으로 제출할 스크린샷:
1. `https://mile.0bin.in` 메인 페이지 캡처 (카카오로 시작하기 버튼 포함)
2. 또는 적립 페이지 `/claim?t=xxx` 캡처 (카카오로 적립하기 버튼 + 개인정보 동의 체크박스 포함)
3. 캡처 시 개인정보(전화번호 등)가 보이면 마스킹 처리
1. `https://mile.0bin.in/signup` 페이지 캡처
- "수집 항목 및 이용 목적" 안내 카드가 보이게
- 전화번호(필수), 이름(필수), 생년월일(선택) 필드가 보이게
- 각 필드 아래 수집 목적 설명이 보이게
- 개인정보 동의 체크박스가 보이게
2. 캡처 시 개인정보(전화번호 등)가 보이면 마스킹 처리
3. 화면이 길면 2장으로 나누어 캡처 (상단: 수집 항목 안내 + 입력 필드, 하단: 동의 + 가입 버튼)