From 6cb64b09982c180f0b1a801750013e3661135e16 Mon Sep 17 00:00:00 2001 From: six-standard Date: Fri, 28 Feb 2025 23:16:20 +0900 Subject: [PATCH 1/9] =?UTF-8?q?modify:=20=EC=BA=90=EC=8B=9C=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=EA=B4=80=EB=A0=A8=20=EC=9D=B4=EC=8A=88=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(with-tracker)/(login)/Content.tsx | 6 ++++++ src/components/auth-required/header/index.tsx | 12 ++++-------- src/utils/revalidateUtil.ts | 9 +++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/utils/revalidateUtil.ts diff --git a/src/app/(with-tracker)/(login)/Content.tsx b/src/app/(with-tracker)/(login)/Content.tsx index 8c63710..3ea96a0 100644 --- a/src/app/(with-tracker)/(login)/Content.tsx +++ b/src/app/(with-tracker)/(login)/Content.tsx @@ -4,10 +4,12 @@ import { useRouter } from 'next/navigation'; import { useForm } from 'react-hook-form'; import Image from 'next/image'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useEffect } from 'react'; import { Input, Button } from '@/components'; import { LoginVo } from '@/types'; import { login, sampleLogin } from '@/apis'; import { trackUserEvent, MessageEnum } from '@/utils/trackUtil'; +import { PATHS } from '@/constants'; const responsiveStyle = "flex items-center gap-5 max-MBI:before:inline-block max-MBI:before:bg-[url('/favicon.png')] max-MBI:before:[background-size:_100%_100%] max-MBI:before:w-16 max-MBI:before:h-16"; @@ -16,6 +18,10 @@ export const Content = () => { const { replace } = useRouter(); const client = useQueryClient(); + useEffect(() => { + if (client.getQueryData([PATHS.ME])) client.removeQueries(); + }, []); + const { register, handleSubmit, diff --git a/src/components/auth-required/header/index.tsx b/src/components/auth-required/header/index.tsx index 6984f7b..d78d879 100644 --- a/src/components/auth-required/header/index.tsx +++ b/src/components/auth-required/header/index.tsx @@ -3,12 +3,13 @@ import { useEffect, useRef, useState } from 'react'; import { usePathname, useRouter } from 'next/navigation'; import Image from 'next/image'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { PATHS, SCREENS } from '@/constants'; import { NameType } from '@/components'; import { useResponsive } from '@/hooks'; import { logout, me } from '@/apis'; import { trackUserEvent, MessageEnum } from '@/utils/trackUtil'; +import { revalidate } from '@/utils/revalidateUtil'; import { defaultStyle, Section, textStyle } from './Section'; const PARAMS = { @@ -33,18 +34,13 @@ export const Header = () => { const router = useRouter(); const width = useResponsive(); const barWidth = width < SCREENS.MBI ? 65 : 180; - const client = useQueryClient(); const { mutate: out } = useMutation({ mutationFn: logout, - onMutate: () => router.replace('/'), - onSuccess: () => client.removeQueries(), + onSuccess: revalidate, }); - const { data: profiles } = useQuery({ - queryKey: [PATHS.ME], - queryFn: me, - }); + const { data: profiles } = useQuery({ queryKey: [PATHS.ME], queryFn: me }); useEffect(() => { const handleClickOutside = (e: MouseEvent) => diff --git a/src/utils/revalidateUtil.ts b/src/utils/revalidateUtil.ts new file mode 100644 index 0000000..375a030 --- /dev/null +++ b/src/utils/revalidateUtil.ts @@ -0,0 +1,9 @@ +'use server'; + +import { revalidatePath } from 'next/cache'; +import { redirect } from 'next/navigation'; + +export async function revalidate() { + revalidatePath('/', 'layout'); + redirect('/'); +} From 5ad21d3f9dd45661b6b2b3dd15d578b5181d9b81 Mon Sep 17 00:00:00 2001 From: six-standard Date: Thu, 6 Mar 2025 09:19:37 +0900 Subject: [PATCH 2/9] =?UTF-8?q?modify:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=9C=EC=97=90=EB=8F=84=20=EC=BA=90=EC=8B=9C=20?= =?UTF-8?q?=EC=9E=94=EC=A1=B4=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(with-tracker)/(login)/Content.tsx | 10 +--------- src/components/auth-required/header/index.tsx | 18 +++++++++++++++--- .../auth-required/main/Section/index.tsx | 15 +++++---------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/app/(with-tracker)/(login)/Content.tsx b/src/app/(with-tracker)/(login)/Content.tsx index 3ea96a0..bad17fb 100644 --- a/src/app/(with-tracker)/(login)/Content.tsx +++ b/src/app/(with-tracker)/(login)/Content.tsx @@ -3,24 +3,17 @@ import { useRouter } from 'next/navigation'; import { useForm } from 'react-hook-form'; import Image from 'next/image'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useEffect } from 'react'; +import { useMutation } from '@tanstack/react-query'; import { Input, Button } from '@/components'; import { LoginVo } from '@/types'; import { login, sampleLogin } from '@/apis'; import { trackUserEvent, MessageEnum } from '@/utils/trackUtil'; -import { PATHS } from '@/constants'; const responsiveStyle = "flex items-center gap-5 max-MBI:before:inline-block max-MBI:before:bg-[url('/favicon.png')] max-MBI:before:[background-size:_100%_100%] max-MBI:before:w-16 max-MBI:before:h-16"; export const Content = () => { const { replace } = useRouter(); - const client = useQueryClient(); - - useEffect(() => { - if (client.getQueryData([PATHS.ME])) client.removeQueries(); - }, []); const { register, @@ -29,7 +22,6 @@ export const Content = () => { } = useForm({ mode: 'all' }); const onSuccess = () => { - client.clear(); trackUserEvent(MessageEnum.LOGIN); replace('/main?asc=false&sort='); }; diff --git a/src/components/auth-required/header/index.tsx b/src/components/auth-required/header/index.tsx index d78d879..530a488 100644 --- a/src/components/auth-required/header/index.tsx +++ b/src/components/auth-required/header/index.tsx @@ -3,13 +3,14 @@ import { useEffect, useRef, useState } from 'react'; import { usePathname, useRouter } from 'next/navigation'; import Image from 'next/image'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { PATHS, SCREENS } from '@/constants'; import { NameType } from '@/components'; import { useResponsive } from '@/hooks'; import { logout, me } from '@/apis'; import { trackUserEvent, MessageEnum } from '@/utils/trackUtil'; import { revalidate } from '@/utils/revalidateUtil'; + import { defaultStyle, Section, textStyle } from './Section'; const PARAMS = { @@ -34,13 +35,24 @@ export const Header = () => { const router = useRouter(); const width = useResponsive(); const barWidth = width < SCREENS.MBI ? 65 : 180; + const client = useQueryClient(); const { mutate: out } = useMutation({ mutationFn: logout, - onSuccess: revalidate, + onSuccess: async () => { + await revalidate(); + client.clear(); + router.replace('/'); + }, }); - const { data: profiles } = useQuery({ queryKey: [PATHS.ME], queryFn: me }); + const { data: profiles } = useQuery({ + queryKey: [PATHS.ME], + queryFn: me, + enabled: !!client.getQueryData([PATHS.ME]), + // 로그아웃 후 리렌더링되어 다시 fetch되는 경우 해결 + // 어차피 prefetch를 통해 데이터를 불러온 상태에서 렌더하기 때문에, 캐시 여부만 판단하면 됨 + }); useEffect(() => { const handleClickOutside = (e: MouseEvent) => diff --git a/src/components/auth-required/main/Section/index.tsx b/src/components/auth-required/main/Section/index.tsx index 91ce6dc..bdb1c45 100644 --- a/src/components/auth-required/main/Section/index.tsx +++ b/src/components/auth-required/main/Section/index.tsx @@ -1,27 +1,22 @@ 'use client'; -import { useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; -import { UserNameNotFoundError } from '@/errors'; import { trackUserEvent, MessageEnum } from '@/utils/trackUtil'; import { parseNumber } from '@/utils/numberUtil'; import { COLORS, env, PATHS } from '@/constants'; import { PostType, UserDto } from '@/types'; import { Icon } from '@/components'; +import { getQueryClient } from '@/utils/queryUtil'; import { Graph } from './Graph'; export const Section = (p: PostType) => { const [open, setOpen] = useState(false); - const client = useQueryClient(); - const { username } = client.getQueryData([PATHS.ME]) as UserDto; - const URL = env.VELOG_URL; + const username = ( + getQueryClient().getQueryData([PATHS.ME]) as Partial + )?.username; - if (!username) { - throw new UserNameNotFoundError(); - } - - const url = `${URL}/@${username}/${p.slug}`; + const url = `${env.VELOG_URL}/@${username}/${p.slug}`; return (
From 8898550b841f258756d58fc65b140173bbadc34b Mon Sep 17 00:00:00 2001 From: six-standard Date: Thu, 6 Mar 2025 09:19:40 +0900 Subject: [PATCH 3/9] =?UTF-8?q?modify:=20=EC=82=AC=EC=86=8C=ED=95=9C=20?= =?UTF-8?q?=ED=9C=B4=EB=A8=BC=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/instance.request.ts | 2 +- src/utils/queryUtil.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apis/instance.request.ts b/src/apis/instance.request.ts index 05ba80b..521bffa 100644 --- a/src/apis/instance.request.ts +++ b/src/apis/instance.request.ts @@ -75,7 +75,7 @@ export const instance = async ( return (data.body as unknown as SuccessType).data; } catch (err: unknown) { const context = err as Response; - if (location && !context.ok && context.status === 403) { + if (location && !context.ok && context.status === 401) { window.location.replace('/'); } //context.status === 401 || diff --git a/src/utils/queryUtil.ts b/src/utils/queryUtil.ts index 9e40e0a..ad54707 100644 --- a/src/utils/queryUtil.ts +++ b/src/utils/queryUtil.ts @@ -3,7 +3,7 @@ import { toast } from 'react-toastify'; let localQueryClient: QueryClient | undefined; const STALE_TIME = 1000 * 60 * 3; -const GC_TIME = 1000; +const GC_TIME = 1000 * 60 * 20; const createQueryClient = () => new QueryClient({ From ba04d998159b3bbcd531f255e31a5ba286681ad1 Mon Sep 17 00:00:00 2001 From: six-standard Date: Thu, 6 Mar 2025 09:20:21 +0900 Subject: [PATCH 4/9] =?UTF-8?q?refactor:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/instance.request.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apis/instance.request.ts b/src/apis/instance.request.ts index 521bffa..613fd03 100644 --- a/src/apis/instance.request.ts +++ b/src/apis/instance.request.ts @@ -78,7 +78,6 @@ export const instance = async ( if (location && !context.ok && context.status === 401) { window.location.replace('/'); } - //context.status === 401 || setContext('Request', { path: context.url, status: context.status, From b49b5d3a26a902346adcdc2aa29dece79b7348a4 Mon Sep 17 00:00:00 2001 From: six-standard Date: Thu, 6 Mar 2025 13:36:39 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feature:=20=EC=84=9C=EB=B2=84=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B8=EB=93=A4=EB=A7=81=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/error/page.tsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/app/error/page.tsx diff --git a/src/app/error/page.tsx b/src/app/error/page.tsx new file mode 100644 index 0000000..329e820 --- /dev/null +++ b/src/app/error/page.tsx @@ -0,0 +1,22 @@ +import Image from 'next/image'; +export default async function Page() { + return ( +
+
+
+
+ +
+ +

+ Velog Dashboard +

+
+ + + 알 수 없는 오류가 발생했습니다. + +
+
+ ); +} From 66115c53f3f506735f5a1039ea779ea8f0bacdb2 Mon Sep 17 00:00:00 2001 From: six-standard Date: Fri, 7 Mar 2025 09:43:48 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feature:=20=EC=88=9C=EC=88=98=20HTML=20?= =?UTF-8?q?=ED=98=95=ED=83=9C=EC=9D=98=20=EC=98=A4=EB=A5=98=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- error.html | 167 +++++++++++++++++++++++++++++++++++++++++ src/app/error/page.tsx | 22 ------ 2 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 error.html delete mode 100644 src/app/error/page.tsx diff --git a/error.html b/error.html new file mode 100644 index 0000000..e830d73 --- /dev/null +++ b/error.html @@ -0,0 +1,167 @@ + + + + + + Document + + + +
+
+
+ VD +
+

Velog Dashboard

+
+ 알 수 없는 오류가 발생했습니다. +
+ + diff --git a/src/app/error/page.tsx b/src/app/error/page.tsx deleted file mode 100644 index 329e820..0000000 --- a/src/app/error/page.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Image from 'next/image'; -export default async function Page() { - return ( -
-
-
-
- -
- -

- Velog Dashboard -

-
- - - 알 수 없는 오류가 발생했습니다. - -
-
- ); -} From 828e4790f1f594b5c24e497952ca05d262512bbb Mon Sep 17 00:00:00 2001 From: six-standard Date: Fri, 7 Mar 2025 09:44:28 +0900 Subject: [PATCH 7/9] =?UTF-8?q?modify:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/package.json b/package.json index 186bf20..5409b0f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@channel.io/channel-web-sdk-loader": "^2.0.0", "@next/third-parties": "^15.1.7", + "@sentry/core": "^9.4.0", "@sentry/nextjs": "^8.47.0", "@tanstack/react-query": "^5.61.3", "@tanstack/react-query-devtools": "^5.62.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29ce0d1..85f3689 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@next/third-parties': specifier: ^15.1.7 version: 15.1.7(next@14.2.18(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@sentry/core': + specifier: ^9.4.0 + version: 9.4.0 '@sentry/nextjs': specifier: ^8.47.0 version: 8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(next@14.2.18(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.97.1) @@ -1499,6 +1502,10 @@ packages: resolution: {integrity: sha512-iSEJZMe3DOcqBFZQAqgA3NB2lCWBc4Gv5x/SCri/TVg96wAlss4VrUunSI2Mp0J4jJ5nJcJ2ChqHSBAU48k3FA==} engines: {node: '>=14.18'} + '@sentry/core@9.4.0': + resolution: {integrity: sha512-Edd/uWDGZ+1CMuVVWdxIOm1rBhzgpqiqz984TZu20wryoIoRsA8ZllUar6N+cWK17VusNY0OS2DozKO69y7fVQ==} + engines: {node: '>=18'} + '@sentry/nextjs@8.47.0': resolution: {integrity: sha512-qr++MBYhyAwF25hGq7LAxe3Xehs+w2V4b8mVxilRYFXNkWFazY1ukZcVzq9pKrrt5uTiURTf68e8eVMraHnHEQ==} engines: {node: '>=14.18'} @@ -6373,6 +6380,8 @@ snapshots: '@sentry/core@8.47.0': {} + '@sentry/core@9.4.0': {} + '@sentry/nextjs@8.47.0(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(next@14.2.18(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.97.1)': dependencies: '@opentelemetry/api': 1.9.0 From f8c3b575b7a31a785e5aabedd896ef768fbecdee Mon Sep 17 00:00:00 2001 From: six-standard Date: Sat, 8 Mar 2025 11:20:23 +0900 Subject: [PATCH 8/9] =?UTF-8?q?modify:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- error.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/error.html b/error.html index e830d73..56939b6 100644 --- a/error.html +++ b/error.html @@ -3,7 +3,7 @@ - Document + Velog Dashboard - 500