Skip to content

Commit ecb2d5a

Browse files
authored
[25.02.16 / TASK-118] Feature: 채널톡 추가 및 헤더 로고 클릭 동작 추가 (#13)
* hotfix: 일부 오류 수정 username 관련 오류 및 SSR 렌더린 관련 오류로 추정되는 오류 수정 * feat: 채널톡 추가 및 헤더 로고 동작 추가 * refactor: console.log 제거 * refactor: 메세지 내용 변경 * refactor: 코멘트 반영 겸 자잘한 오류 해결 * refactor: username 관련 코드 수정 * refactor: ENV 오류 전용 공통 오류 객체 추가
1 parent 6fc9c58 commit ecb2d5a

File tree

20 files changed

+111
-65
lines changed

20 files changed

+111
-65
lines changed

.env.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ NEXT_PUBLIC_BASE_URL=<'server url here'>
44
NEXT_PUBLIC_VELOG_URL=https://velog.io
55
NEXT_PUBLIC_ABORT_MS=<'abort time(ms) for fetch here'>
66
SENTRY_AUTH_TOKEN=<'sentry auth token here'>
7+
NEXT_PUBLIC_CHANNELTALK_PLUGIN_KEY=<'channelTalk plugin key here'>
78
NEXT_PUBLIC_EVENT_LOG=<'Whether to send an event log here (true | false)'>
89
SENTRY_DSN=<'sentry dsn here'>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"test": "jest"
1414
},
1515
"dependencies": {
16+
"@channel.io/channel-web-sdk-loader": "^2.0.0",
1617
"@sentry/nextjs": "^8.47.0",
1718
"@tanstack/react-query": "^5.61.3",
1819
"@tanstack/react-query-devtools": "^5.62.11",

pnpm-lock.yaml

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

src/apis/dashboard.request.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
import { PostDetailDto, PostListDto, PostSummaryDto } from '@/types';
22
import { PATHS } from '@/constants';
3-
import { InitType, instance } from './instance.request';
3+
import { instance } from './instance.request';
44

55
type SortType = {
66
asc: boolean;
77
sort: string;
88
};
99

10-
export const postList = async (
11-
props: InitType<PostListDto>,
12-
sort: SortType,
13-
cursor?: string,
14-
) =>
10+
export const postList = async (sort: SortType, cursor?: string) =>
1511
await instance<null, PostListDto>(
1612
cursor
1713
? `${PATHS.POSTS}?cursor=${cursor}&asc=${sort.asc}&sort=${sort.sort}`
1814
: `${PATHS.POSTS}?asc=${sort.asc}&sort=${sort.sort}`,
19-
props,
2015
);
2116

22-
export const postSummary = async (props: InitType<PostSummaryDto>) =>
23-
await instance<null, PostSummaryDto>(PATHS.SUMMARY, props);
17+
export const postSummary = async () =>
18+
await instance<null, PostSummaryDto>(PATHS.SUMMARY);
2419

2520
export const postDetail = async (path: string, start: string, end: string) =>
2621
await instance<null, PostDetailDto>(

src/apis/instance.request.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import returnFetch, { FetchArgs } from 'return-fetch';
22

33
import { captureException, setContext } from '@sentry/nextjs';
4-
import { ServerNotRespondingError } from '@/errors';
4+
import { EnvNotFoundError, ServerNotRespondingError } from '@/errors';
55

66
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
77
const ABORT_MS = Number(process.env.NEXT_PUBLIC_ABORT_MS);
88

99
if (Number.isNaN(ABORT_MS)) {
10-
throw new Error('ABORT_MS가 ENV에서 설정되지 않았습니다');
10+
throw new EnvNotFoundError('ABORT_MS');
1111
}
1212

1313
if (!BASE_URL) {
14-
throw new Error('BASE_URL이 ENV에서 설정되지 않았습니다.');
14+
throw new EnvNotFoundError('BASE_URL');
1515
}
1616

1717
type ErrorType = {
@@ -60,9 +60,20 @@ export const instance = async <I, R>(
6060
init?: InitType<I>,
6161
error?: Record<string, Error>,
6262
): Promise<R> => {
63+
let cookieHeader = '';
64+
if (typeof window === 'undefined') {
65+
cookieHeader = (await import('next/headers')).cookies().toString();
66+
}
67+
6368
try {
6469
const data = await fetch('/api' + input, {
6570
...init,
71+
headers: cookieHeader
72+
? {
73+
...init?.headers,
74+
Cookie: cookieHeader,
75+
}
76+
: init?.headers,
6677
body: init?.body ? JSON.stringify(init.body) : undefined,
6778
signal: AbortSignal.timeout
6879
? AbortSignal.timeout(ABORT_MS)

src/apis/user.request.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NotFoundError } from '@/errors';
22
import { PATHS } from '@/constants';
33
import { LoginVo, UserDto } from '@/types';
4-
import { InitType, instance } from './instance.request';
4+
import { instance } from './instance.request';
55

66
export const login = async (body: LoginVo) =>
77
await instance(
@@ -15,8 +15,7 @@ export const login = async (body: LoginVo) =>
1515
},
1616
);
1717

18-
export const me = async (props: InitType<UserDto>) =>
19-
await instance<null, UserDto>(PATHS.ME, props);
18+
export const me = async () => await instance<null, UserDto>(PATHS.ME);
2019

2120
export const logout = async () =>
2221
await instance(PATHS.LOGOUT, { method: 'POST', body: undefined });

src/app/(with-tracker)/(auth-required)/layout.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { ReactElement } from 'react';
22
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
3-
import { cookies } from 'next/headers';
43
import { Header } from '@/components';
54
import { PATHS } from '@/constants';
6-
import { getCookieForAuth } from '@/utils/cookieUtil';
75
import { me } from '@/apis';
86
import { getQueryClient } from '@/utils/queryUtil';
97

@@ -16,8 +14,7 @@ export default async function Layout({ children }: IProp) {
1614

1715
await client.prefetchQuery({
1816
queryKey: [PATHS.ME],
19-
queryFn: async () =>
20-
await me(getCookieForAuth(cookies, ['access_token', 'refresh_token'])),
17+
queryFn: me,
2118
});
2219

2320
return (

src/app/(with-tracker)/(auth-required)/main/Content.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export const Content = () => {
3030
queryKey: [PATHS.POSTS, [searchParams.asc, searchParams.sort]], // Query Key
3131
queryFn: async ({ pageParam = '' }) =>
3232
await postList(
33-
{},
3433
{ asc: searchParams.asc === 'true', sort: searchParams.sort || '' },
3534
pageParam,
3635
),
@@ -41,7 +40,7 @@ export const Content = () => {
4140

4241
const { data: summaries } = useQuery({
4342
queryKey: [PATHS.SUMMARY],
44-
queryFn: async () => await postSummary({}),
43+
queryFn: postSummary,
4544
});
4645

4746
useEffect(() => {

src/app/(with-tracker)/(auth-required)/main/page.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
22
import { Metadata } from 'next';
3-
import { cookies } from 'next/headers';
43
import { PATHS } from '@/constants';
54
import { postList, postSummary } from '@/apis';
6-
import { getCookieForAuth } from '@/utils/cookieUtil';
75
import { getQueryClient } from '@/utils/queryUtil';
86
import { Content } from './Content';
97

@@ -25,22 +23,16 @@ export default async function Page({ searchParams }: IProp) {
2523
await client.prefetchInfiniteQuery({
2624
queryKey: [PATHS.POSTS, [searchParams.asc, searchParams.sort]],
2725
queryFn: async () =>
28-
await postList(
29-
getCookieForAuth(cookies, ['access_token', 'refresh_token']),
30-
{
31-
asc: searchParams.asc === 'true',
32-
sort: searchParams.sort || '',
33-
},
34-
),
26+
await postList({
27+
asc: searchParams.asc === 'true',
28+
sort: searchParams.sort || '',
29+
}),
3530
initialPageParam: undefined,
3631
});
3732

3833
await client.prefetchQuery({
3934
queryKey: [PATHS.SUMMARY],
40-
queryFn: async () =>
41-
await postSummary(
42-
getCookieForAuth(cookies, ['access_token', 'refresh_token']),
43-
),
35+
queryFn: postSummary,
4436
});
4537

4638
return (

src/app/layout.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as sentry from '@sentry/nextjs';
55
import type { Metadata } from 'next';
66
import { ReactNode } from 'react';
77
import './globals.css';
8-
import { QueryProvider } from '@/components';
8+
import { ChannelTalkProvider, QueryProvider } from '@/components';
99

1010
export const metadata: Metadata = {
1111
title: 'Velog Dashboard',
@@ -23,8 +23,10 @@ export default function RootLayout({
2323
<body className={`${NotoSansKr.className} w-full bg-BG-MAIN`}>
2424
<sentry.ErrorBoundary>
2525
<QueryProvider>
26-
<ToastContainer autoClose={2000} />
27-
{children}
26+
<ChannelTalkProvider>
27+
<ToastContainer autoClose={2000} />
28+
{children}
29+
</ChannelTalkProvider>
2830
</QueryProvider>
2931
</sentry.ErrorBoundary>
3032
</body>

0 commit comments

Comments
 (0)