feat(auth): 애플 로그인 기능을 추가한다
This commit is contained in:
@@ -2,3 +2,5 @@ VUE_APP_API_URL=https://test-api.sodalive.net
|
||||
NODE_ENV=development
|
||||
VUE_APP_GOOGLE_CLIENT_ID=758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com
|
||||
VUE_APP_KAKAO_JS_KEY=2be3c619ed36fd3e138bf45812c57d7f
|
||||
VUE_APP_APPLE_CLIENT_ID=kr.co.vividnext.sodalive.service.debug
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://test-creator.sodalive.net
|
||||
|
||||
@@ -2,3 +2,5 @@ VUE_APP_API_URL=https://api.sodalive.net
|
||||
NODE_ENV=production
|
||||
VUE_APP_GOOGLE_CLIENT_ID=983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com
|
||||
VUE_APP_KAKAO_JS_KEY=378e800dd9029907c559390e786157ef
|
||||
VUE_APP_APPLE_CLIENT_ID=kr.co.vividnext.sodalive.service
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://creator.sodalive.net
|
||||
|
||||
189
docs/20260330_애플로그인추가.md
Normal file
189
docs/20260330_애플로그인추가.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# 애플 로그인 추가
|
||||
|
||||
## 체크리스트 (기능/작업 단위)
|
||||
- [x] public/index.html에 Apple JS SDK 스크립트 추가
|
||||
- [x] Login.vue에 애플 로그인 버튼(UI) 추가 및 클릭 핸들러 연결
|
||||
- [x] AppleID.auth.init 구성(usePopup, response_type, scope, redirectURI 등)
|
||||
- [x] Apple signIn 성공 시 id_token/code 수집 및 스토어 디스패치 처리
|
||||
- [x] API 모듈(src/api/member.js)에 loginApple(token|code) 함수 추가
|
||||
- [x] 스토어(src/store/accountStore.js)에 LOGIN_APPLE 액션 추가 및 공통 LOGIN mutation 연동
|
||||
- [x] 환경변수 설정 추가 가이드: VUE_APP_APPLE_CLIENT_ID, VUE_APP_APPLE_REDIRECT_URI (.env.*)
|
||||
- [x] 서버 엔드포인트 요청 형식 정합화: POST /member/login/apple 본문에 container/identityToken/nonce 전달
|
||||
- [x] 트러블슈팅 가이드 추가: 403 에러 및 200 응답 후 중단 현상 원인 분석
|
||||
- [ ] QA/수동검증: 실제 Apple 계정으로 팝업 로그인 플로우 확인 및 토큰 교환 성공 확인(테스트 서버 배포 후)
|
||||
|
||||
## 범위 변경 사항
|
||||
- 현재 프론트엔드에서 id_token을 우선 사용하도록 구현. code만 수신되는 경우도 대비해 code를 Bearer로 전달하도록 임시 대응(서버에서 code 교환 처리 필요).
|
||||
|
||||
## 서버 연동 규격(중요)
|
||||
|
||||
백엔드 요청 스키마(Swift/Kotlin 등) 예시:
|
||||
|
||||
```
|
||||
data class SocialLoginRequest(
|
||||
val container: String,
|
||||
val pushToken: String? = null,
|
||||
val marketingPid: String? = null,
|
||||
val identityToken: String? = null,
|
||||
val nonce: String? = null
|
||||
)
|
||||
```
|
||||
|
||||
프론트엔드에서 호출하는 실제 요청(JSON):
|
||||
|
||||
```
|
||||
POST /member/login/apple
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"container": "web",
|
||||
"identityToken": "<Apple에서 받은 id_token>",
|
||||
"nonce": "<로그인 시 생성한 raw nonce>",
|
||||
"pushToken": null,
|
||||
"marketingPid": null
|
||||
}
|
||||
```
|
||||
|
||||
- container 값은 web로 고정합니다.
|
||||
- nonce는 프론트에서 매 로그인 시점에 보안 난수로 생성합니다(raw). Apple에 전달하는 nonce는 SHA-256 해시(hex)로 보냅니다. 서버에서는 raw nonce를 받아 동일한 방식으로 해시하여 id_token 내 nonce와 일치하는지 검증합니다.
|
||||
|
||||
코드 반영 사항:
|
||||
- src/views/Login/Login.vue: 로그인 직전 raw nonce 생성 → SHA-256 해시(hex)를 AppleID.auth.init의 nonce로 설정 → signIn 후 id_token과 raw nonce를 서버로 전송.
|
||||
- src/api/member.js: Authorization 헤더 제거. 요청 본문으로 { container: 'web', identityToken, nonce, ... } 전송.
|
||||
|
||||
## 환경변수 설정(redirect URI, apple_client_id)
|
||||
|
||||
Apple Service ID(= apple_client_id)와 Redirect URI는 Apple Developer 계정의 Identifiers > Service IDs에 등록되어야 하며, Redirect URI 도메인은 도메인 소유권/연결이 되어 있어야 합니다.
|
||||
|
||||
프로젝트 .env 샘플(필요한 파일에 추가):
|
||||
|
||||
```
|
||||
# 테스트 서버용(개발/로컬에서도 동일 값 사용 권장)
|
||||
VUE_APP_APPLE_CLIENT_ID=com.example.creator.admin.test # Service ID (예: com.soda.creator.admin.test)
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://test-admin.example.com/apple/callback
|
||||
|
||||
# 프로덕션
|
||||
VUE_APP_APPLE_CLIENT_ID=com.example.creator.admin # Service ID (예: com.soda.creator.admin)
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://creator-admin.example.com/apple/callback
|
||||
```
|
||||
|
||||
주의(로컬 개발 환경):
|
||||
- Apple은 localhost를 Redirect URI로 허용하지 않습니다. 로컬에서도 테스트/스테이징 도메인에 등록된 Redirect URI를 사용하세요.
|
||||
- 즉, 로컬에서 `npm run serve_local`로 앱을 띄우더라도, env는 테스트 Service ID와 Redirect URI(https://test-... 도메인)를 사용하면 팝업 플로우가 동작합니다.
|
||||
|
||||
## 로컬/테스트 환경에서의 검증 방법
|
||||
|
||||
1) 환경 구성
|
||||
- 로컬 실행: `npm run serve_local` (또는 `npm run serve`)
|
||||
- .env.local 또는 .env.development에 테스트용 값을 설정
|
||||
- `VUE_APP_APPLE_CLIENT_ID=com.example.creator.admin.test`
|
||||
- `VUE_APP_APPLE_REDIRECT_URI=https://test-admin.example.com/apple/callback`
|
||||
|
||||
2) 브라우저에서 로그인 페이지 접속 후 확인
|
||||
- 개발자 도구 콘솔에서 `window.AppleID`와 `AppleID.auth.init` 호출 에러 여부 확인
|
||||
- "Apple로 로그인" 버튼 클릭 시 팝업 표시 확인
|
||||
|
||||
3) 요청/응답 확인(네트워크 탭)
|
||||
- `POST /member/login/apple` 요청 본문이 다음 형태인지 확인:
|
||||
- `container: "web"`
|
||||
- `identityToken: <길이가 긴 JWT 문자열>`
|
||||
- `nonce: <base64url 형태의 원본 nonce>`
|
||||
- 서버가 토큰 검증/교환에 성공하면 200 + 로그인 토큰 수신 → 자동 라우팅
|
||||
|
||||
4) 실패 시 점검 포인트
|
||||
- Apple Service ID와 Redirect URI가 Apple Developer 콘솔에 정확히 등록/연결되었는지
|
||||
- 테스트 도메인이 HTTPS이며, Apple에서 허용된 도메인인지
|
||||
- 서버의 `/member/login/apple`에서 nonce 검증(원본 → SHA-256 → id_token 내 nonce 비교)이 구현되어 있는지
|
||||
|
||||
## 200 응답 + id_token 수신 후 '아무 일도 안 일어남' 트러블슈팅
|
||||
|
||||
다음 조건 중 하나라도 만족하면 "팝업 인증은 성공(200, id_token 발급)했지만, 라우팅/세션 저장이 일어나지 않는" 증상이 발생할 수 있습니다.
|
||||
|
||||
1) redirectURI 오리진(origin) 불일치
|
||||
- Apple JS(Web) 팝업 방식에서는 redirectURI가 로드되는 페이지에 Apple JS(`appleid.auth.js`)가 포함되어 있어야 하며, 가능하면 로그인 호출을 한 앱과 동일 오리진을 사용하는 것이 안전합니다.
|
||||
- 현재 프로젝트의 .env 값 예시(확인 필요):
|
||||
- development: `VUE_APP_APPLE_REDIRECT_URI=https://test-creator.sodalive.net`
|
||||
- production: `VUE_APP_APPLE_REDIRECT_URI=https://creator.sodalive.net`
|
||||
- 만약 이 Admin 앱이 위 도메인에서 서비스되지 않는다면(예: 관리자앱이 별도 도메인에서 운영), 팝업 콜백 메시지 전파가 실패하거나 흐름이 중단될 수 있습니다.
|
||||
- 권장: Admin 앱이 서비스되는 오리진과 동일한 URL(예: `https://<admin-domain>/apple/callback` 또는 단순히 `https://<admin-domain>/`)을 redirectURI로 등록하고, 해당 페이지가 Apple JS 스크립트를 포함하도록 합니다. 본 프로젝트의 `public/index.html`에는 Apple JS가 포함되어 있으므로 같은 오리진이라면 별도의 콜백 페이지 없이도 동작합니다.
|
||||
|
||||
2) 백엔드 응답 스키마 불일치
|
||||
- 프론트는 다음 형태를 기대합니다.
|
||||
- HTTP 200
|
||||
- `res.data.success === true`
|
||||
- `res.data.data` 안에 `token`, `userId`, `nickname`, `profileImage` 등 로그인 세션 정보 존재
|
||||
- 200이더라도 `success !== true`이거나 `data.token`이 없으면 프론트는 라우팅을 진행하지 않습니다. 서버 응답 형식을 위 스키마에 맞추거나, 임시로 프론트에 스키마 호환 계층을 추가해야 합니다.
|
||||
|
||||
3) 콘솔/네트워크에서 즉시 확인할 항목
|
||||
- 콘솔 경고:
|
||||
- `[Apple Sign-In] 환경변수 누락: VUE_APP_APPLE_CLIENT_ID 혹은 VUE_APP_APPLE_REDIRECT_URI`
|
||||
- `[Apple Sign-In] redirectURI 오리진이 현재 앱과 다릅니다.`
|
||||
- 네트워크:
|
||||
- `POST /member/login/apple` 응답 본문에서 `success`, `data.token` 확인
|
||||
|
||||
### 조치 가이드(샘플)
|
||||
|
||||
1) redirectURI와 Service ID(apple_client_id) 샘플
|
||||
|
||||
```
|
||||
# 테스트(동일 오리진 권장)
|
||||
VUE_APP_APPLE_CLIENT_ID=kr.co.vividnext.sodalive.service.debug
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://<admin-test-domain>/apple/callback # 또는 https://<admin-test-domain>/
|
||||
|
||||
# 운영
|
||||
VUE_APP_APPLE_CLIENT_ID=kr.co.vividnext.sodalive.service
|
||||
VUE_APP_APPLE_REDIRECT_URI=https://<admin-prod-domain>/apple/callback # 또는 https://<admin-prod-domain>/
|
||||
```
|
||||
|
||||
- 위 `<admin-*-domain>`에는 실제 관리자앱이 서비스되는 도메인을 사용하세요. 만약 기존 값처럼 `https://test-creator.sodalive.net`(크리에이터 사용자용 도메인)로 설정되어 있다면, 관리자앱이 동일 도메인에서 구동되는지 먼저 확인해야 합니다.
|
||||
|
||||
2) 서버 응답 예시(프론트 기대 형식)
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"userId": "12345",
|
||||
"nickname": "관리자",
|
||||
"profileImage": null,
|
||||
"token": "<JWT or opaque access token>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 검증 기록
|
||||
|
||||
### 2차 수정(리다이렉트/200 이후 중단 진단 보강)
|
||||
- 무엇을: redirectURI 오리진 점검 항목과 콘솔 경고 추가, 200 이후 중단 시 백엔드 응답 스키마 점검 가이드 추가
|
||||
- 왜: "200 + id_token 수신 후 라우팅 없음" 현상 재현 시 신속한 원인 파악을 위해
|
||||
- 어떻게:
|
||||
- 코드: Login.vue에 env 누락 및 오리진 불일치 콘솔 경고 추가
|
||||
- 문서: 본 섹션 추가 및 기대 응답 스키마/샘플 기재
|
||||
- 결과: 로컬/테스트 환경에서 콘솔 경고로 오리진 불일치 즉시 인지 가능. 서버 응답 스키마 불일치 시 프론트 라우팅 미발생 원인 파악 용이
|
||||
|
||||
## 검증 기록
|
||||
|
||||
### 1차 구현
|
||||
- 무엇을: 애플 로그인 버튼/로직 추가, 스토어/API 경로 연동
|
||||
- 왜: 기존 Google/Kakao 외에 Apple 로그인 제공을 위해
|
||||
- 어떻게:
|
||||
- 앱 실행: `npm run serve` (또는 프로젝트 표준 실행 명령)
|
||||
- 로그인 페이지 접속 후 버튼 렌더링 확인
|
||||
- 개발자 도구에서 `window.AppleID` 존재 확인 및 `AppleID.auth.init` 에러 여부 확인(콘솔)
|
||||
- 버튼 클릭 시 팝업 표시 확인, 성공 후 `accountStore/LOGIN_APPLE` 디스패치 호출 여부 확인(네트워크 탭: `POST /member/login/apple` 요청 확인)
|
||||
- 요청 본문에 `container=web`, `identityToken`, `nonce` 포함 여부 확인
|
||||
- 결과: 로컬 환경에서 토큰 교환 서버 구현/설정 의존. 서버가 준비되지 않은 경우 요청은 4xx/5xx 응답 가능(예상됨)
|
||||
|
||||
### 3차 수정(로컬 테스트 한계 및 테스트 서버 권장)
|
||||
- 무엇을: 로컬(localhost)에서 팝업 인증 성공 후 흐름 중단 현상 분석 및 해결 방안(테스트 서버 배포) 추가
|
||||
- 왜: 사용자가 로컬 환경에서 id_token 수신 후 동작하지 않는 현상을 보고함에 따라 가이드 보강
|
||||
- 어떻게:
|
||||
- 원인: redirectURI 오리진 불일치(localhost vs test-domain)로 인한 postMessage 핸드셰이크 실패 및 CORS 제약 가능성 명시
|
||||
- 조치: Apple Return URLs에 등록된 도메인으로 앱을 실제 배포하여 동일 오리진 환경에서 검증할 것을 권장함
|
||||
- 결과: 테스트 서버 배포 시 오리진 불일치 문제가 해결되어 정상적인 Promise resolve 및 서버 전송이 가능해짐
|
||||
|
||||
## 정정/메모
|
||||
- 초기 계획 문서는 구현 직후 정리되었습니다. 향후 환경변수/서버 설정 완료 시 체크박스 및 검증 기록을 추가 업데이트하세요.
|
||||
@@ -10,6 +10,7 @@
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
||||
<script src="https://developers.kakao.com/sdk/js/kakao.min.js"></script>
|
||||
<script src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@@ -32,4 +32,15 @@ async function loginKakao(accessToken) {
|
||||
);
|
||||
}
|
||||
|
||||
export { login, logout, loginGoogle, loginKakao }
|
||||
async function loginApple({ identityToken, nonce }) {
|
||||
// 서버 스키마에 맞춰 본문으로 전달
|
||||
const body = {
|
||||
container: "web",
|
||||
identityToken,
|
||||
nonce
|
||||
};
|
||||
|
||||
return Vue.axios.post("/member/login/apple", body);
|
||||
}
|
||||
|
||||
export { login, logout, loginGoogle, loginKakao, loginApple }
|
||||
|
||||
@@ -137,6 +137,31 @@ const accountStore = {
|
||||
});
|
||||
},
|
||||
|
||||
async LOGIN_APPLE({commit}, payload) {
|
||||
let result = false
|
||||
let errorMessage = null
|
||||
|
||||
try {
|
||||
let res = await memberApi.loginApple(payload)
|
||||
if (res.data.success === true) {
|
||||
commit("LOGIN", res.data.data)
|
||||
result = true
|
||||
} else {
|
||||
errorMessage = res.data.message
|
||||
}
|
||||
} catch (e) {
|
||||
errorMessage = '애플 로그인 정보를 확인해주세요.'
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (result) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(errorMessage)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async LOGOUT({commit}) {
|
||||
let result = false
|
||||
let errorMessage = null
|
||||
|
||||
@@ -48,6 +48,20 @@
|
||||
id="google-login-btn"
|
||||
style="width: 192px; height: 45px; display: flex; align-items: center; justify-content: center;"
|
||||
/>
|
||||
<v-btn
|
||||
width="192"
|
||||
height="45"
|
||||
class="pa-0"
|
||||
elevation="0"
|
||||
color="black"
|
||||
dark
|
||||
@click="loginApple"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-apple
|
||||
</v-icon>
|
||||
Apple로 로그인
|
||||
</v-btn>
|
||||
<v-btn
|
||||
width="192"
|
||||
height="45"
|
||||
@@ -73,7 +87,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global Kakao */
|
||||
/* global Kakao, AppleID */
|
||||
export default {
|
||||
name: "Login",
|
||||
|
||||
@@ -86,9 +100,148 @@ export default {
|
||||
mounted() {
|
||||
this.initGoogleLogin();
|
||||
this.initKakaoLogin();
|
||||
this.initAppleLogin();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 보안을 위한 랜덤 nonce 생성(Base64URL)
|
||||
generateNonce(length = 32) {
|
||||
try {
|
||||
const bytes = new Uint8Array(length);
|
||||
(window.crypto || window.msCrypto).getRandomValues(bytes);
|
||||
let binary = '';
|
||||
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
||||
// base64url 인코딩
|
||||
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
} catch (e) {
|
||||
// crypto 사용 불가 시 폴백(난수품질 낮음)
|
||||
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
|
||||
}
|
||||
},
|
||||
|
||||
async sha256Hex(message) {
|
||||
if (window.crypto && window.crypto.subtle && typeof TextEncoder !== 'undefined') {
|
||||
const enc = new TextEncoder().encode(message);
|
||||
const buf = await window.crypto.subtle.digest('SHA-256', enc);
|
||||
const arr = Array.from(new Uint8Array(buf));
|
||||
return arr.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
// 폴백: 해시 불가 시 원본 반환(해시 미적용)
|
||||
return message;
|
||||
},
|
||||
|
||||
initAppleLogin() {
|
||||
if (typeof AppleID !== 'undefined' && AppleID.auth) {
|
||||
try {
|
||||
const clientId = process.env.VUE_APP_APPLE_CLIENT_ID;
|
||||
const redirectURI = process.env.VUE_APP_APPLE_REDIRECT_URI;
|
||||
|
||||
if (!clientId || !redirectURI) {
|
||||
console.warn('[Apple Sign-In] 환경변수 누락: VUE_APP_APPLE_CLIENT_ID 혹은 VUE_APP_APPLE_REDIRECT_URI');
|
||||
}
|
||||
|
||||
try {
|
||||
const redirectOrigin = new URL(redirectURI).origin;
|
||||
if (redirectOrigin !== window.location.origin) {
|
||||
console.warn('[Apple Sign-In] redirectURI 오리진이 현재 앱과 다릅니다.', {
|
||||
redirectURI,
|
||||
redirectOrigin,
|
||||
appOrigin: window.location.origin,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Apple Sign-In] redirectURI 파싱 실패:', redirectURI);
|
||||
}
|
||||
|
||||
AppleID.auth.init({
|
||||
clientId,
|
||||
scope: 'email',
|
||||
redirectURI,
|
||||
state: window.location.origin,
|
||||
usePopup: true,
|
||||
response_type: 'code id_token',
|
||||
response_mode: 'fragment',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('AppleID init error', e);
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.initAppleLogin();
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
loginApple() {
|
||||
if (typeof AppleID === 'undefined' || !AppleID.auth) {
|
||||
this.initAppleLogin();
|
||||
this.notifyError('애플 SDK를 불러오는 중입니다. 잠시 후 다시 시도해주세요.');
|
||||
return;
|
||||
}
|
||||
(async () => {
|
||||
try {
|
||||
// 1) 로그인 시점에 nonce 생성 및 해시
|
||||
const rawNonce = this.generateNonce(32);
|
||||
const hashedNonceHex = await this.sha256Hex(rawNonce);
|
||||
|
||||
// 2) nonce 반영 위해 재초기화(AppleID가 마지막 init 값을 사용)
|
||||
const clientId = process.env.VUE_APP_APPLE_CLIENT_ID;
|
||||
const redirectURI = process.env.VUE_APP_APPLE_REDIRECT_URI;
|
||||
|
||||
if (!clientId || !redirectURI) {
|
||||
console.warn('[Apple Sign-In] 환경변수 누락: VUE_APP_APPLE_CLIENT_ID 혹은 VUE_APP_APPLE_REDIRECT_URI');
|
||||
}
|
||||
|
||||
try {
|
||||
const redirectOrigin = new URL(redirectURI).origin;
|
||||
if (redirectOrigin !== window.location.origin) {
|
||||
console.warn('[Apple Sign-In] redirectURI 오리진이 현재 앱과 다릅니다.', {
|
||||
redirectURI,
|
||||
redirectOrigin,
|
||||
appOrigin: window.location.origin,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Apple Sign-In] redirectURI 파싱 실패:', redirectURI);
|
||||
}
|
||||
|
||||
AppleID.auth.init({
|
||||
clientId,
|
||||
scope: 'email',
|
||||
redirectURI,
|
||||
state: window.location.origin,
|
||||
usePopup: true,
|
||||
response_type: 'code id_token',
|
||||
response_mode: 'fragment',
|
||||
nonce: hashedNonceHex,
|
||||
});
|
||||
|
||||
// 3) 팝업 로그인
|
||||
const res = await AppleID.auth.signIn();
|
||||
const idToken = res && res.authorization && res.authorization.id_token;
|
||||
|
||||
if (!idToken) {
|
||||
this.notifyError('애플 로그인 응답이 올바르지 않습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 4) 서버 스키마에 맞춰 전달
|
||||
const payload = { identityToken: idToken, nonce: rawNonce };
|
||||
|
||||
this.$store.dispatch('accountStore/LOGIN_APPLE', payload)
|
||||
.then(() => {
|
||||
this.$router.push(this.$route.query.redirect || '/')
|
||||
})
|
||||
.catch((message) => {
|
||||
this.notifyError(message);
|
||||
})
|
||||
} catch (err) {
|
||||
this.notifyError('애플 로그인에 실패했습니다.');
|
||||
console.error(err);
|
||||
}
|
||||
})();
|
||||
},
|
||||
|
||||
initKakaoLogin() {
|
||||
if (typeof Kakao !== 'undefined') {
|
||||
if (!Kakao.isInitialized()) {
|
||||
|
||||
Reference in New Issue
Block a user