diff --git a/backend/app.py b/backend/app.py
index 2ce3c45..8ea3bb9 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -745,6 +745,40 @@ def claim_kakao_start():
return redirect(auth_url)
+def _handle_mypage_kakao_callback(code, kakao_client):
+ """마이페이지 카카오 콜백 처리 - 카카오 ID로 유저 조회 후 마이페이지 이동"""
+ success, token_data = kakao_client.get_access_token(code)
+ if not success:
+ return render_template('error.html', message="카카오 인증에 실패했습니다.")
+
+ access_token = token_data.get('access_token')
+ success, user_info = kakao_client.get_user_info(access_token)
+ if not success:
+ return render_template('error.html', message="카카오 사용자 정보를 가져올 수 없습니다.")
+
+ kakao_id = user_info.get('kakao_id')
+ kakao_phone_raw = user_info.get('phone_number')
+ kakao_phone = normalize_kakao_phone(kakao_phone_raw)
+
+ # 1) 카카오 ID로 기존 유저 조회
+ existing_user_id = find_user_by_kakao_id(kakao_id)
+ if existing_user_id:
+ conn = db_manager.get_sqlite_connection()
+ cursor = conn.cursor()
+ cursor.execute("SELECT phone FROM users WHERE id = ?", (existing_user_id,))
+ row = cursor.fetchone()
+ if row:
+ return redirect(f"/my-page?phone={row['phone']}")
+
+ # 2) 카카오에서 전화번호를 받은 경우
+ if kakao_phone:
+ return redirect(f"/my-page?phone={kakao_phone}")
+
+ # 3) 둘 다 없으면 전화번호 입력 안내
+ return render_template('error.html',
+ message="카카오 계정에 연결된 적립 정보가 없습니다. 전화번호로 조회해주세요.")
+
+
@app.route('/claim/kakao/callback')
def claim_kakao_callback():
"""카카오 OAuth 콜백 - 토큰 교환 → 사용자 정보 → 적립 처리"""
@@ -772,6 +806,10 @@ def claim_kakao_callback():
if csrf_token != session.pop('kakao_csrf', None):
return render_template('error.html', message="보안 검증에 실패했습니다. 다시 시도해주세요.")
+ # 2.5 마이페이지 조회 목적이면 별도 처리
+ if state_data.get('purpose') == 'mypage':
+ return _handle_mypage_kakao_callback(code, get_kakao_client())
+
# 3. claim 컨텍스트 복원
token_param = state_data.get('t', '')
parts = token_param.split(':')
@@ -894,6 +932,28 @@ def api_claim_kakao():
})
+@app.route('/my-page/kakao/start')
+def mypage_kakao_start():
+ """마이페이지 카카오 로그인 조회"""
+ from services.kakao_client import get_kakao_client
+
+ csrf_token = secrets.token_hex(16)
+ state_data = {
+ 'purpose': 'mypage',
+ 'csrf': csrf_token
+ }
+ state_encoded = base64.urlsafe_b64encode(
+ json.dumps(state_data).encode()
+ ).decode()
+
+ session['kakao_csrf'] = csrf_token
+
+ kakao_client = get_kakao_client()
+ auth_url = kakao_client.get_authorization_url(state=state_encoded)
+
+ return redirect(auth_url)
+
+
@app.route('/my-page')
def my_page():
"""마이페이지 (전화번호로 조회)"""
diff --git a/backend/services/kakao_client.py b/backend/services/kakao_client.py
index f3f7457..43db429 100644
--- a/backend/services/kakao_client.py
+++ b/backend/services/kakao_client.py
@@ -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,phone_number'
+ 'scope': 'profile_nickname,profile_image,account_email'
}
if state:
diff --git a/backend/templates/my_page.html b/backend/templates/my_page.html
index 3d1ff56..545a954 100644
--- a/backend/templates/my_page.html
+++ b/backend/templates/my_page.html
@@ -272,6 +272,10 @@
diff --git a/docs/kakao-troubleshooting.md b/docs/kakao-troubleshooting.md
new file mode 100644
index 0000000..161f975
--- /dev/null
+++ b/docs/kakao-troubleshooting.md
@@ -0,0 +1,168 @@
+# 카카오 OAuth 트러블슈팅 가이드
+
+## 에러 코드 목록
+
+### KOE101 - 앱 관리자 설정 오류 / 플랫폼키 에러
+
+**원인**: `KAKAO_CLIENT_ID`(REST API 키)가 비어있거나 잘못된 값
+
+**해결**:
+1. `backend/.env` 파일이 존재하는지 확인
+2. `KAKAO_CLIENT_ID` 값이 올바른지 확인
+3. 카카오 개발자 콘솔 > 앱 > 플랫폼 키 > REST API 키에서 확인
+
+```bash
+# backend/.env
+KAKAO_CLIENT_ID=caad27ac4bc92d8dc83bdd6aae744811
+```
+
+### KOE205 - Invalid scope
+
+**원인**: 요청한 OAuth scope가 앱에서 허용되지 않음
+
+**해결**:
+- `name`, `phone_number` scope는 **비즈앱 심사** 후 동의항목에서 별도 활성화 필요
+- 기본 scope만 사용: `profile_nickname,profile_image,account_email`
+- 파일: `backend/services/kakao_client.py` > `get_authorization_url()` > `scope` 파라미터
+
+**동의항목 활성화 경로**:
+```
+카카오 개발자 콘솔 > 카카오 로그인 > 동의항목
+```
+
+| scope | 비즈앱 필요 | 현재 상태 |
+|-------|-----------|----------|
+| profile_nickname | X | 사용 중 |
+| profile_image | X | 사용 중 |
+| account_email | X | 사용 중 |
+| name | O | 미사용 (비즈앱 심사 필요) |
+| phone_number | O | 미사용 (비즈앱 심사 필요) |
+
+### KOE320 - authorization code not found
+
+**원인**: 카카오 authorization code가 만료되었거나 이미 사용됨
+
+**해결**:
+- code는 **1회용**이며 발급 후 수 분 내 사용해야 함
+- 브라우저 새로고침으로 같은 code를 재사용하면 발생
+- 사용자에게 다시 카카오 로그인하도록 안내
+
+### UnboundLocalError: kakao_client
+
+**원인**: 콜백 핸들러에서 `kakao_client` 변수가 초기화되기 전에 접근
+
+**상황**: 마이페이지 카카오 조회 시, 콜백 함수 상단의 `purpose == 'mypage'` 분기에서
+`kakao_client` 변수가 아직 할당되지 않은 상태에서 사용
+
+**해결**: `get_kakao_client()` 함수를 직접 호출
+```python
+# 잘못된 코드
+if state_data.get('purpose') == 'mypage':
+ return _handle_mypage_kakao_callback(code, kakao_client) # UnboundLocalError
+
+# 올바른 코드
+if state_data.get('purpose') == 'mypage':
+ return _handle_mypage_kakao_callback(code, get_kakao_client())
+```
+
+### ModuleNotFoundError: No module named 'requests'
+
+**원인**: `requests` 라이브러리 미설치
+
+**해결**:
+```bash
+pip install requests
+```
+
+## 카카오 개발자 콘솔 주의사항
+
+### Redirect URI 위치 (2025년 12월 개편)
+
+**현재 경로** (2025.12~):
+```
+앱 > 플랫폼 키 > REST API 키 클릭 > 리다이렉트 URI
+```
+
+**혼동하기 쉬운 위치**:
+- `카카오 로그인 > 일반` - 여기에는 더 이상 Redirect URI 없음 (Webhook만 있음)
+- `카카오 로그인 > 고급 > 로그아웃 리다이렉트 URI` - 로그아웃용이므로 혼동 주의
+
+자세한 설정 가이드: [kakao-oauth-setup.md](./kakao-oauth-setup.md)
+
+### Client Secret 위치
+
+```
+앱 > 플랫폼 키 > REST API 키 클릭 > 클라이언트 시크릿
+```
+
+활성화 상태 확인 필수. 비활성화 시 토큰 교환 실패.
+
+## 환경변수 체크리스트
+
+```bash
+# backend/.env (git에 포함되지 않음)
+KAKAO_CLIENT_ID=
+KAKAO_CLIENT_SECRET=<클라이언트 시크릿 코드>
+KAKAO_REDIRECT_URI=https://mile.0bin.in/claim/kakao/callback
+```
+
+## 로컬 개발 환경 주의사항
+
+로컬(192.168.0.14:7001)에서 테스트 시:
+1. 카카오 버튼 클릭 → 카카오 로그인 화면 (정상)
+2. 로그인 후 `https://mile.0bin.in/claim/kakao/callback`로 리다이렉트됨
+3. 로컬이 아닌 **운영 서버**로 돌아감
+
+로컬에서 완전한 테스트를 하려면:
+- `KAKAO_REDIRECT_URI`를 `http://192.168.0.14:7001/claim/kakao/callback`로 변경
+- 카카오 콘솔에도 해당 URI 등록 필요
+- 테스트 후 반드시 원래 값으로 복원
+
+## 데이터 흐름
+
+### 카카오 적립 (QR 스캔 → 카카오 로그인)
+
+```
+QR 스캔 → /claim?t=txn:nonce
+ → "카카오로 적립하기" 클릭
+ → /claim/kakao/start?t=txn:nonce
+ → state={t, csrf} 인코딩 → 카카오 OAuth
+ → /claim/kakao/callback
+ → phone_number 없음 → claim_kakao_phone.html (전화번호 입력)
+ → POST /api/claim/kakao (세션의 kakao_data + 폼의 phone)
+ → get_or_create_user → link_kakao_identity → claim_mileage
+ → 성공 화면
+```
+
+### 카카오 마이페이지 조회
+
+```
+/my-page → "카카오로 조회하기" 클릭
+ → /my-page/kakao/start
+ → state={purpose:"mypage", csrf} 인코딩 → 카카오 OAuth
+ → /claim/kakao/callback (동일 콜백 URL 재사용)
+ → purpose=="mypage" 감지 → _handle_mypage_kakao_callback()
+ → find_user_by_kakao_id(kakao_id)
+ → 유저 발견 → /my-page?phone=xxx 리다이렉트
+ → 유저 없음 → "카카오 계정에 연결된 적립 정보가 없습니다" 에러
+```
+
+### customer_identities 테이블 매핑
+
+```
+카카오 적립 시:
+ kakao_id="12345678" + phone="01021307390"
+ → users 테이블: user_id=7
+ → customer_identities: {user_id=7, provider="kakao", provider_id="12345678"}
+
+카카오 조회 시:
+ kakao_id="12345678"
+ → customer_identities에서 user_id=7 조회
+ → users 테이블에서 phone="01021307390" 조회
+ → /my-page?phone=01021307390
+```
+
+---
+
+**작성일**: 2026-02-25
+**관련 문서**: [kakao-oauth-setup.md](./kakao-oauth-setup.md)