feat: 카카오 로그인 마이페이지 조회 + scope/env 수정 + 트러블슈팅 문서
- 마이페이지에 카카오 로그인 조회 기능 추가 (/my-page/kakao/start) - 콜백 핸들러에 purpose=mypage 분기 추가 (동일 콜백 URL 재사용) - my_page_login.html에 "카카오로 조회하기" 버튼 추가 - my_page.html 헤더에 카카오 조회 버튼 추가 - OAuth scope에서 name, phone_number 제거 (비즈앱 심사 미완료) - KOE101/KOE205/KOE320 등 에러별 트러블슈팅 문서 작성 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
31cf6e3816
commit
eb44701410
@ -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():
|
||||
"""마이페이지 (전화번호로 조회)"""
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -272,6 +272,10 @@
|
||||
<div class="header-top">
|
||||
<div class="header-title">마이페이지</div>
|
||||
<a href="/my-page" class="btn-logout">다른 번호로 조회</a>
|
||||
<a href="/my-page/kakao/start" class="btn-logout" style="display: flex; align-items: center; gap: 4px; background: #FEE500; color: #191919; padding: 6px 12px; border-radius: 8px; font-size: 12px; font-weight: 600;">
|
||||
<svg width="12" height="12" viewBox="0 0 20 20" fill="none"><path d="M10 1C4.477 1 0 4.477 0 8.5c0 2.58 1.693 4.847 4.243 6.134l-1.084 3.97a.3.3 0 00.457.338L7.7 16.392c.75.112 1.52.17 2.3.17 5.523 0 10-3.477 10-7.562C20 4.477 15.523 1 10 1z" fill="#191919"/></svg>
|
||||
카카오 조회
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-profile">
|
||||
|
||||
@ -167,6 +167,20 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- 또는 구분선 -->
|
||||
<div style="text-align: center; margin: 24px 0 20px 0; position: relative;">
|
||||
<span style="background: #fff; padding: 0 16px; color: #adb5bd; font-size: 13px; font-weight: 500; position: relative; z-index: 1;">또는</span>
|
||||
<div style="position: absolute; top: 50%; left: 0; right: 0; height: 1px; background: #e9ecef; z-index: 0;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 카카오 로그인 버튼 -->
|
||||
<a href="/my-page/kakao/start" style="display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; padding: 18px; background: #FEE500; color: #191919; border: none; border-radius: 14px; font-size: 17px; font-weight: 700; cursor: pointer; letter-spacing: -0.3px; text-decoration: none; transition: all 0.2s ease;">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10 1C4.477 1 0 4.477 0 8.5c0 2.58 1.693 4.847 4.243 6.134l-1.084 3.97a.3.3 0 00.457.338L7.7 16.392c.75.112 1.52.17 2.3.17 5.523 0 10-3.477 10-7.562C20 4.477 15.523 1 10 1z" fill="#191919"/>
|
||||
</svg>
|
||||
카카오로 조회하기
|
||||
</a>
|
||||
|
||||
<a href="/" class="btn-back">← 홈으로</a>
|
||||
</div>
|
||||
|
||||
|
||||
168
docs/kakao-troubleshooting.md
Normal file
168
docs/kakao-troubleshooting.md
Normal file
@ -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=<REST API 키>
|
||||
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)
|
||||
Loading…
Reference in New Issue
Block a user