Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/client/src/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Outlet,useLocation } from 'react-router-dom';
import { Outlet, useLocation } from 'react-router-dom';
import { Sidebar } from '@shared/components/sidebar/Sidebar';

const Layout = () => {
const location = useLocation();
const isOnboarding = location.pathname.startsWith('/onboarding');
const isLogin = location.pathname.startsWith('/login');

return (
<>
<div className="flex h-screen">
{!isOnboarding && <Sidebar />}
{!isOnboarding && !isLogin && <Sidebar />}
<main className="bg-gray-bg flex-1 overflow-y-auto">
<Outlet />
</main>
Expand Down
73 changes: 73 additions & 0 deletions apps/client/src/pages/login/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import onBoardingBg from '/assets/onBoarding/background/onBoardingBg.webp';
import Header from '@pages/onBoarding/components/header/Header';
import Footer from '@pages/onBoarding/components/footer/Footer';
import { Icon } from '@pinback/design-system/icons';
import { handleGoogleLogin } from '@shared/utils/handleGoogleLogin';
import { Link } from 'react-router-dom';
import Chippi from '@assets/chippi_extension_popup.svg';
import GoogleLogo from '/assets/onBoarding/icons/googleLogo.svg';

const Login = () => {
return (
<div
className="relative flex h-screen w-screen flex-col bg-cover bg-center bg-no-repeat"
style={{ backgroundImage: `url(${onBoardingBg})` }}
>
<Header />

<main className="flex w-full flex-1 items-center justify-center">
<div className="bg-white-bg flex h-[54.8rem] w-[63.2rem] flex-col items-center justify-center rounded-[2.4rem]">
<img
src={Chippi}
alt="치삐 이미지"
className="mb-[1.6rem] h-[19.4rem] w-[19.4rem] object-contain"
/>

<Icon name={'logo'} height={34} width={123} className="mb-[1.6rem]" />

<h1 className="head2 text-font-black-1 mb-[0.8rem] text-center">
가장 재미있게 북마크를 활용하는 방법
</h1>

<p className="body2-m text-font-gray-3 mb-[3.5rem] text-center">
내가 저장해둔 영감을 pinback과 함께 열어볼까요?
</p>

<button
onClick={handleGoogleLogin}
className="sub2-sb flex h-[5.2rem] w-[29.8rem] items-center justify-between gap-3 rounded-full border border-gray-100 bg-white px-[2rem]"
>
<img
src={GoogleLogo}
alt="구글 로고"
className="h-[2.435rem] w-[2.435rem]"
/>
구글 계정으로 로그인/회원가입
</button>

<p className="text-font-gray-3 caption2-m mt-[2.4rem] text-center">
가입 시 pinback의{' '}
<Link
to="/terms"
className="underline underline-offset-2 hover:opacity-70"
>
이용 약관
</Link>{' '}
및{' '}
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
개인정보처리방침
</Link>
에 동의한 것으로 간주됩니다.
</p>
</div>
</main>

<Footer />
</div>
);
};

export default Login;
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
import Chippi from '@assets/chippi_extension_popup.svg';
import GoogleLogo from '/assets/onBoarding/icons/googleLogo.svg';
import { Link } from 'react-router-dom';
import { handleGoogleLogin } from '@shared/utils/handleGoogleLogin';

const SocialLoginStep = () => {
const handleGoogleLogin = () => {
const clientId = import.meta.env.VITE_GOOGLE_CLIENT_ID;

const redirectUri = import.meta.env.PROD
? import.meta.env.VITE_GOOGLE_REDIRECT_URI_PROD
: import.meta.env.VITE_GOOGLE_REDIRECT_URI_DEV;

if (!clientId || !redirectUri) {
alert('Google OAuth 설정이 누락되었습니다.');
return;
}
const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${clientId}` +
`&redirect_uri=${redirectUri}` +
`&response_type=code` +
`&scope=email profile`;

window.location.href = googleAuthUrl;
};

return (
<div className="flex flex-col items-center justify-center pt-6">
<div className="flex h-full flex-col items-center justify-center">
<img
src={Chippi}
alt="치삐 이미지"
Expand All @@ -50,6 +31,24 @@ const SocialLoginStep = () => {
/>
구글 계정으로 로그인
</button>
{/*TODO: 개인정보처리방침 추가되면 링크 수정*/}
<p className="text-font-gray-3 caption2-m mt-[2.4rem] text-center">
가입 시 pinback의{' '}
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
이용 약관
</Link>{' '}
및{' '}
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
개인정보처리방침
</Link>
Comment on lines +37 to +49
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

링크 경로 불일치를 수정하세요.

"이용 약관"과 "개인정보처리방침" 링크가 모두 /privacy를 가리키고 있습니다. Login.tsx (50-55번째 줄)에서는 "이용 약관"이 /terms로, "개인정보처리방침"이 /privacy로 올바르게 분리되어 있습니다. 일관성을 위해 여기서도 동일하게 수정해야 합니다.

🔎 제안하는 수정 사항
       <Link
-          to="/privacy"
+          to="/terms"
           className="underline underline-offset-2 hover:opacity-70"
       >
         이용 약관
       </Link>{' '}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
이용 약관
</Link>{' '}
{' '}
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
개인정보처리방침
</Link>
<Link
to="/terms"
className="underline underline-offset-2 hover:opacity-70"
>
이용 약관
</Link>{' '}
{' '}
<Link
to="/privacy"
className="underline underline-offset-2 hover:opacity-70"
>
개인정보처리방침
</Link>
🤖 Prompt for AI Agents
In @apps/client/src/pages/onBoarding/components/funnel/step/SocialLoginStep.tsx
around lines 37-49, Update the two Link components in SocialLoginStep that
render "이용 약관" and "개인정보처리방침": change the first Link (the one wrapping the text
"이용 약관") so its to prop points to "/terms" instead of "/privacy", and keep the
second Link (the one wrapping "개인정보처리방침") pointing to "/privacy" so the routes
match Login.tsx.

에 동의한 것으로 간주됩니다.
</p>
</div>
);
};
Expand Down
5 changes: 5 additions & 0 deletions apps/client/src/routes/router.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Category from '@pages/category/Category';
import Level from '@pages/level/Level';
import Login from '@pages/login/Login';
import MyBookmark from '@pages/myBookmark/MyBookmark';
import GoogleCallback from '@pages/onBoarding/GoogleCallback';
import OnBoarding from '@pages/onBoarding/OnBoarding';
Expand Down Expand Up @@ -37,6 +38,10 @@ export const router = createBrowserRouter([
path: ROUTES_CONFIG.onBoardingCallback.path,
element: <GoogleCallback />,
},
{
path: ROUTES_CONFIG.login.path,
element: <Login />,
},
],
},
]);
4 changes: 4 additions & 0 deletions apps/client/src/routes/routesConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ export const ROUTES_CONFIG = {
title: '구글 OAuth 콜백',
path: '/onboarding/callback',
},
login: {
title: '로그인',
path: '/login',
},
};
5 changes: 4 additions & 1 deletion apps/client/src/shared/apis/setting/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ apiRequest.interceptors.response.use(
originalRequest.url?.includes(url)
);

const isLoginPage = window.location.pathname.startsWith('/login');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 분리한 상수 써도 될 것 같아요!


if (
error.response &&
(error.response.status === 401 || error.response.status === 403) &&
!originalRequest._retry &&
!isNoAuth
!isNoAuth &&
!isLoginPage
) {
originalRequest._retry = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function ProfilePopup({
const handleLogout = () => {
localStorage.removeItem('token');
localStorage.removeItem('email');
navigate('/onboarding');
navigate('/login');
};

return (
Expand Down
20 changes: 20 additions & 0 deletions apps/client/src/shared/utils/handleGoogleLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const handleGoogleLogin = () => {
const clientId = import.meta.env.VITE_GOOGLE_CLIENT_ID;

const redirectUri = import.meta.env.PROD
? import.meta.env.VITE_GOOGLE_REDIRECT_URI_PROD
: import.meta.env.VITE_GOOGLE_REDIRECT_URI_DEV;

if (!clientId || !redirectUri) {
alert('Google OAuth 설정이 누락되었습니다.');
return;
}
const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${clientId}` +
`&redirect_uri=${redirectUri}` +
`&response_type=code` +
`&scope=email profile`;

window.location.href = googleAuthUrl;
Comment on lines +12 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

URL 파라미터를 인코딩해야 합니다.

clientIdredirectUri를 URL에 직접 삽입하고 있지만, 특수 문자가 포함된 경우 URL이 깨지거나 보안 문제가 발생할 수 있습니다. 모든 파라미터는 encodeURIComponent()로 인코딩되어야 합니다.

🔎 제안하는 수정 사항
   const googleAuthUrl =
     `https://accounts.google.com/o/oauth2/v2/auth?` +
-    `client_id=${clientId}` +
-    `&redirect_uri=${redirectUri}` +
+    `client_id=${encodeURIComponent(clientId)}` +
+    `&redirect_uri=${encodeURIComponent(redirectUri)}` +
     `&response_type=code` +
-    `&scope=email profile`;
+    `&scope=${encodeURIComponent('email profile')}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${clientId}` +
`&redirect_uri=${redirectUri}` +
`&response_type=code` +
`&scope=email profile`;
window.location.href = googleAuthUrl;
const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${encodeURIComponent(clientId)}` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&response_type=code` +
`&scope=${encodeURIComponent('email profile')}`;
window.location.href = googleAuthUrl;
🤖 Prompt for AI Agents
In @apps/client/src/shared/utils/handleGoogleLogin.ts around lines 12-19, The
constructed googleAuthUrl in handleGoogleLogin uses raw clientId and redirectUri
which can break URLs; update the string building for googleAuthUrl to encode
clientId and redirectUri with encodeURIComponent (also encode scope/other params
if dynamic), e.g. replace direct ${clientId} and ${redirectUri} with encoded
values, then assign window.location.href = googleAuthUrl as before so the
redirect URL is safe.

};
Loading