Skip to content

Commit 7ace24d

Browse files
authored
[25.05.08 / TASK-139] Feature - QR 로그인 (#31)
1 parent 38d9604 commit 7ace24d

File tree

10 files changed

+73
-10
lines changed

10 files changed

+73
-10
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@tanstack/react-query": "^5.69.0",
2929
"chart.js": "^4.4.7",
3030
"next": "14.2.18",
31+
"qrcode.react": "^4.2.0",
3132
"react": "^18",
3233
"react-chartjs-2": "^5.2.0",
3334
"react-dom": "^18",

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/apis/user.request.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ export const me = async () => await instance<null, UserDto>(PATHS.ME);
1717
export const logout = async () => await instance(PATHS.LOGOUT, { method: 'POST', body: undefined });
1818

1919
export const sampleLogin = async () => await instance(PATHS.SAMPLELOGIN, { method: 'POST' });
20+
21+
export const createQRToken = async () =>
22+
await instance<null, { token: string }>(PATHS.QRLOGIN, { method: 'POST' });
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use client';
2+
3+
import { QRCodeSVG } from 'qrcode.react';
4+
import { useQuery } from '@tanstack/react-query';
5+
import { COLORS, env, PATHS, SCREENS } from '@/constants';
6+
import { useResponsive } from '@/hooks';
7+
import { createQRToken } from '@/apis';
8+
import { Modal as Layout } from '@/components';
9+
10+
export const QRCode = () => {
11+
const width = useResponsive();
12+
13+
const { data, isSuccess } = useQuery({ queryKey: [PATHS.QRLOGIN], queryFn: createQRToken });
14+
15+
return (
16+
<Layout title="QR 로그인" className="w-fit h-fit">
17+
{isSuccess ? (
18+
<QRCodeSVG
19+
value={`${env.BASE_URL}/api/qr-login?token=${data.token}`}
20+
width={width < SCREENS.MBI ? 140 : 181}
21+
height={width < SCREENS.MBI ? 140 : 181}
22+
enableBackground={0}
23+
bgColor={COLORS.BG.SUB}
24+
fgColor={COLORS.TEXT.MAIN}
25+
className="transition-all"
26+
/>
27+
) : (
28+
<div className="size-[180px] max-MBI:size-[140px] bg-BG-ALT" />
29+
)}
30+
</Layout>
31+
);
32+
};

src/app/(auth-required)/components/header/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { logout, me } from '@/apis';
1212
import { useModal } from '@/hooks/useModal';
1313
import { defaultStyle, Section, textStyle } from './Section';
1414
import { Modal } from '../notice/Modal';
15+
import { QRCode } from '../QRCode';
1516

1617
const PARAMS = {
1718
MAIN: '?asc=false&sort=',
@@ -105,14 +106,23 @@ export const Header = () => {
105106
<div className="w-0 h-0 border-[15px] ml-3 mr-3 border-TRANSPARENT border-b-BG-SUB" />
106107
<div className="cursor-pointer h-fit flex-col rounded-[4px] bg-BG-SUB shadow-BORDER-MAIN shadow-md">
107108
<button
108-
className="text-DESTRUCTIVE-SUB text-I3 p-5 max-MBI:p-4 flex whitespace-nowrap w-auto hover:bg-BG-ALT"
109+
className="text-DESTRUCTIVE-SUB text-I3 p-5 max-MBI:p-4 flex whitespace-nowrap w-full justify-center hover:bg-BG-ALT"
109110
onClick={() => {
110111
start();
111112
out();
112113
}}
113114
>
114115
로그아웃
115116
</button>
117+
<button
118+
className="text-TEXT-MAIN text-I3 p-5 max-MBI:p-4 flex items-center justify-center whitespace-nowrap w-full hover:bg-BG-ALT"
119+
onClick={() => {
120+
setOpen(false);
121+
ModalOpen(<QRCode />);
122+
}}
123+
>
124+
QR 로그인
125+
</button>
116126
</div>
117127
</div>
118128
)}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './header';
2+
export * from './notice';
3+
export * from './QRCode';

src/app/(auth-required)/components/notice/index.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ export const Notice = () => {
1919

2020
useEffect(() => {
2121
try {
22-
const lastUpdated = new Date(data?.posts[0].created_at?.split('T')[0] as string).getTime();
22+
if (!data?.posts[0]) return;
2323

24+
const lastUpdated = new Date(data?.posts[0].created_at?.split('T')[0] as string).getTime();
2425
const daysSinceUpdate = Math.ceil((new Date().getTime() - lastUpdated) / DAY_IN_MS);
2526

26-
if (daysSinceUpdate <= RECENT_POST_THRESHOLD_DAYS) {
27-
const expiry = localStorage.getItem(NOTIFICATION_STORAGE_KEY);
28-
if (!expiry || parseInt(expiry, 10) < new Date().getTime()) {
29-
setShow(true);
30-
}
27+
if (daysSinceUpdate > RECENT_POST_THRESHOLD_DAYS) return;
28+
29+
const expiry = localStorage.getItem(NOTIFICATION_STORAGE_KEY);
30+
if (!expiry || parseInt(expiry, 10) < new Date().getTime()) {
31+
setShow(true);
3132
}
3233
} catch (error) {
3334
console.error('알림 날짜 처리 중 오류 발생:', error);
Lines changed: 2 additions & 2 deletions
Loading

src/components/Modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const Modal = ({ title, children, ...rest }: IProp) => {
2424
<div
2525
{...rest}
2626
className={twMerge(
27-
'overflow-auto flex flex-col gap-3 p-10 max-MBI:p-7 rounded-md bg-BG-SUB',
27+
'overflow-auto flex flex-col gap-5 p-10 max-MBI:p-7 rounded-md bg-BG-SUB',
2828
rest.className,
2929
)}
3030
>

src/constants/paths.constant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const PATHS = {
22
LOGIN: '/login',
33
SAMPLELOGIN: '/login-sample',
4+
QRLOGIN: '/qr-login',
45
NOTIS: '/notis',
56
EVENT: '/event',
67
STAY: '/stay',

0 commit comments

Comments
 (0)