[feat] solution/frontend: 구글 버튼을 로그인 관문에도 · 문구 한글화
"구글 로그인 버튼이 없다" 는 지적이 맞았다. 버튼을 LoginPage·SignupPage 에만 붙여 뒀는데,
이 앱에서 사장님이 실제로 로그인 화면을 만나는 자리는 **에디터 진입 관문**(EditorSignInGate →
SignInForm)이다. 정작 거기엔 없었다.
- features/auth/SignInForm: 구글 버튼 추가. 관문·로그인 화면이 같은 폼을 쓰므로 한 곳만 고치면 된다
- lib/googleIdentity: 스크립트를 ?hl=ko 로 받는다. renderButton 의 locale 옵션은 안 먹었다 —
'ko'·'ko_KR' 둘 다 'Continue with Google' 이 그대로 나왔다(실측)
- 버튼 문구는 signin_with('Google 계정으로 로그인'). 가입 화면만 signup_with 로 둔다.
문구 자체는 고를 수 없다 — 구글 브랜드 가이드라 GIS 가 주는 번역을 그대로 쓴다
브라우저 확인(localhost:80): 로그인 화면에 'Google 계정으로 로그인' 한글 노출.
tsc·eslint·vite build 통과.
This commit is contained in:
parent
d376677b86
commit
128596fc74
@ -11,7 +11,7 @@ import {GOOGLE_CLIENT_ID, isGoogleLoginEnabled, loadGoogleIdentity} from '@/lib/
|
|||||||
*/
|
*/
|
||||||
export function GoogleSignInButton({
|
export function GoogleSignInButton({
|
||||||
onCredential,
|
onCredential,
|
||||||
text = 'continue_with',
|
text = 'signin_with',
|
||||||
}: {
|
}: {
|
||||||
onCredential: (credential: string) => void;
|
onCredential: (credential: string) => void;
|
||||||
text?: 'continue_with' | 'signin_with' | 'signup_with';
|
text?: 'continue_with' | 'signin_with' | 'signup_with';
|
||||||
@ -46,7 +46,9 @@ export function GoogleSignInButton({
|
|||||||
size: 'large',
|
size: 'large',
|
||||||
shape: 'rectangular',
|
shape: 'rectangular',
|
||||||
text,
|
text,
|
||||||
locale: 'ko',
|
// ★ 'ko' 로는 영문('Continue with Google')이 그대로 나왔다. 지역까지 줘야 한국어다.
|
||||||
|
// 문구는 우리가 못 정한다 — 구글 브랜드 가이드라 GIS 가 주는 번역을 그대로 쓴다.
|
||||||
|
locale: 'ko_KR',
|
||||||
logo_alignment: 'center',
|
logo_alignment: 'center',
|
||||||
// GIS 는 숫자 px 만 받는다(최대 400). 컨테이너 폭이 잡히기 전이면 최소값으로 그린다.
|
// GIS 는 숫자 px 만 받는다(최대 400). 컨테이너 폭이 잡히기 전이면 최소값으로 그린다.
|
||||||
width: holder.current.offsetWidth || 320,
|
width: holder.current.offsetWidth || 320,
|
||||||
|
|||||||
@ -6,10 +6,12 @@
|
|||||||
*/
|
*/
|
||||||
import {useState, type FormEvent, type ReactNode} from 'react';
|
import {useState, type FormEvent, type ReactNode} from 'react';
|
||||||
import {LogIn} from 'lucide-react';
|
import {LogIn} from 'lucide-react';
|
||||||
import {login} from '@/api';
|
import {googleLogin, login} from '@/api';
|
||||||
|
import {GoogleSignInButton} from '@/components/auth/GoogleSignInButton';
|
||||||
import {Button} from '@/components/ui/button';
|
import {Button} from '@/components/ui/button';
|
||||||
import {Input} from '@/components/ui/input';
|
import {Input} from '@/components/ui/input';
|
||||||
import {notifyApiError} from '@/lib/notify';
|
import {notifyApiError} from '@/lib/notify';
|
||||||
|
import {isGoogleLoginEnabled} from '@/lib/googleIdentity';
|
||||||
import {establishSession} from '@/lib/session';
|
import {establishSession} from '@/lib/session';
|
||||||
|
|
||||||
interface SignInFormProps {
|
interface SignInFormProps {
|
||||||
@ -26,6 +28,27 @@ export function SignInForm({header, footer, submitLabel = '로그인', onSignedI
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
// 구글은 가입과 로그인이 같은 동작이다 — 처음 온 계정이면 백엔드가 그 자리에서 만든다.
|
||||||
|
const handleGoogle = async (credential: string) => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
const res = await googleLogin({credential});
|
||||||
|
if (res.result?.success === false) {
|
||||||
|
notifyApiError({data: res}, '구글 로그인에 실패했습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(await establishSession(res, ''))) {
|
||||||
|
notifyApiError({data: res}, '로그인 응답에 토큰이 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSignedIn?.();
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError(error, '구글 로그인에 실패했습니다.');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (event: FormEvent) => {
|
const handleSubmit = async (event: FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
@ -89,6 +112,19 @@ export function SignInForm({header, footer, submitLabel = '로그인', onSignedI
|
|||||||
<span>{submitLabel}</span>
|
<span>{submitLabel}</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{/* ★ 로그인 화면이 여기 하나만 있는 게 아니다 — 에디터 관문·2단계도 이 폼을 쓴다.
|
||||||
|
구글 버튼을 LoginPage 에만 붙여 두면 정작 사장님이 만나는 자리엔 없다. */}
|
||||||
|
{isGoogleLoginEnabled() && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="h-px flex-1 bg-border" />
|
||||||
|
<span className="text-[11px] text-muted-foreground">또는</span>
|
||||||
|
<span className="h-px flex-1 bg-border" />
|
||||||
|
</div>
|
||||||
|
<GoogleSignInButton onCredential={handleGoogle} text="signin_with" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{footer}
|
{footer}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,7 +8,9 @@
|
|||||||
* ★ client_id 는 비밀이 아니다(번들에 그대로 들어간다). 이 값으로 할 수 있는 건 "우리 앱 앞으로"
|
* ★ client_id 는 비밀이 아니다(번들에 그대로 들어간다). 이 값으로 할 수 있는 건 "우리 앱 앞으로"
|
||||||
* 토큰을 받는 것뿐이고, 그 토큰이 우리 계정이 되려면 백엔드의 aud 대조를 통과해야 한다.
|
* 토큰을 받는 것뿐이고, 그 토큰이 우리 계정이 되려면 백엔드의 aud 대조를 통과해야 한다.
|
||||||
*/
|
*/
|
||||||
const SCRIPT_URL = 'https://accounts.google.com/gsi/client';
|
// ★ 언어는 **스크립트 URL 의 hl** 로 잡는다. renderButton 의 locale 옵션은 안 먹었다
|
||||||
|
// (locale:'ko'·'ko_KR' 둘 다 'Continue with Google' 이 그대로 나왔다 — 실측).
|
||||||
|
const SCRIPT_URL = 'https://accounts.google.com/gsi/client?hl=ko';
|
||||||
const SCRIPT_ID = 'google-identity-services';
|
const SCRIPT_ID = 'google-identity-services';
|
||||||
|
|
||||||
export const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID ?? '';
|
export const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID ?? '';
|
||||||
|
|||||||
@ -135,7 +135,7 @@ export function LoginPage({selfServe = true}: {selfServe?: boolean}) {
|
|||||||
<span className="text-[11px] text-muted-foreground">또는</span>
|
<span className="text-[11px] text-muted-foreground">또는</span>
|
||||||
<span className="h-px flex-1 bg-border" />
|
<span className="h-px flex-1 bg-border" />
|
||||||
</div>
|
</div>
|
||||||
<GoogleSignInButton onCredential={handleGoogle} text="continue_with" />
|
<GoogleSignInButton onCredential={handleGoogle} text="signin_with" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user