Skip to content

Commit f1222a6

Browse files
authored
Merge pull request #19 from Check-Data-Out/dev
[25.02.28] 릴리즈 - Dev
2 parents 5abfe69 + 60920e6 commit f1222a6

26 files changed

+218
-108
lines changed

.env.sample

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
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'>
6-
SENTRY_AUTH_TOKEN=<'sentry auth token here'>
6+
NEXT_PUBLIC_SENTRY_AUTH_TOKEN=<'sentry auth token here'>
77
NEXT_PUBLIC_CHANNELTALK_PLUGIN_KEY=<'channelTalk plugin key here'>
8+
NEXT_PUBLIC_GA_ID=<'Google Analytics ID here'>
89
NEXT_PUBLIC_EVENT_LOG=<'Whether to send an event log here (true | false)'>
9-
SENTRY_DSN=<'sentry dsn here'>
10+
NEXT_PUBLIC_SENTRY_DSN=<'sentry dsn here'>

.github/workflows/docker-publish.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ jobs:
3030
echo "NEXT_PUBLIC_VELOG_URL=${{ secrets.NEXT_PUBLIC_VELOG_URL }}" >> .env
3131
echo "NEXT_PUBLIC_ABORT_MS=${{ secrets.NEXT_PUBLIC_ABORT_MS }}" >> .env
3232
echo "NEXT_PUBLIC_EVENT_LOG=${{ secrets.NEXT_PUBLIC_EVENT_LOG }}" >> .env
33-
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env
33+
echo "NEXT_PUBLIC_SENTRY_AUTH_TOKEN=${{ secrets.NEXT_PUBLIC_SENTRY_AUTH_TOKEN }}" >> .env
3434
echo "NEXT_PUBLIC_CHANNELTALK_PLUGIN_KEY=${{ secrets.NEXT_PUBLIC_CHANNELTALK_PLUGIN_KEY }}" >> .env
35-
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
35+
echo "NEXT_PUBLIC_GA_ID=${{ secrets.NEXT_PUBLIC_GA_ID }}" >> .env
36+
echo "NEXT_PUBLIC_SENTRY_DSN=${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}" >> .env
3637
cp .env .env.production
3738
3839
- name: Build Next.js application

eslint.config.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default [
4444
'@typescript-eslint/promise-function-async': 'error',
4545
'@typescript-eslint/consistent-type-assertions': 'error',
4646
'@typescript-eslint/naming-convention': 'off',
47-
'no-restricted-imports': ['error', { patterns: ['..*'] }],
47+
'no-restricted-imports': ['warn', { patterns: ['..*'] }],
4848
},
4949
languageOptions: {
5050
parserOptions: { project: true, tsconfigRootDir: import.meta.dirname },

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@channel.io/channel-web-sdk-loader": "^2.0.0",
17+
"@next/third-parties": "^15.1.7",
1718
"@sentry/nextjs": "^8.47.0",
1819
"@tanstack/react-query": "^5.61.3",
1920
"@tanstack/react-query-devtools": "^5.62.11",

pnpm-lock.yaml

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/opengraph-image.png

99.7 KB
Loading

sentry.client.config.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from '@sentry/nextjs';
6+
import { env } from '@/constants';
67

78
Sentry.init({
8-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9+
dsn: env.SENTRY_DSN,
910

1011
// Add optional integrations for additional features
1112
integrations: [
1213
Sentry.replayIntegration({ maskAllText: false, blockAllMedia: false }),
1314
],
1415

1516
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
16-
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1,
17+
tracesSampleRate: env.NODE_ENV === 'production' ? 0.1 : 1,
1718

1819
// Define how likely Replay events are sampled.
1920
// This sets the sample rate to be 10%. You may want this to be 100% while

sentry.edge.config.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
55

66
import * as Sentry from '@sentry/nextjs';
7+
import { env } from '@/constants';
78

89
Sentry.init({
9-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
10+
dsn: env.SENTRY_DSN,
1011

1112
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
12-
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.05 : 1,
13+
tracesSampleRate: env.NODE_ENV === 'production' ? 0.05 : 1,
1314

1415
// Setting this option to true will print useful information to the console while you're setting up Sentry.
1516
debug: false,

sentry.server.config.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from '@sentry/nextjs';
6+
import { env } from '@/constants';
67

78
Sentry.init({
8-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9+
dsn: env.SENTRY_DSN,
910

1011
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
11-
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1,
12+
tracesSampleRate: env.NODE_ENV === 'production' ? 0.1 : 1,
1213

1314
// Setting this option to true will print useful information to the console while you're setting up Sentry.
1415
debug: false,

src/__mock__/handlers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { http } from 'msw';
2-
import { PATHS } from '@/constants';
2+
import { env, PATHS } from '@/constants';
33
import { LoginVo } from '@/types';
44
import { BaseError, BaseSuccess } from './responses';
55

6-
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL + '/api';
6+
const BASE_URL = env.BASE_URL + '/api';
77

88
const login = http.post(`${BASE_URL}${PATHS.LOGIN}`, async ({ request }) => {
99
const { accessToken, refreshToken } = (await request.json()) as LoginVo;

src/apis/instance.request.ts

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import returnFetch, { FetchArgs } from 'return-fetch';
22

33
import { captureException, setContext } from '@sentry/nextjs';
4-
import { EnvNotFoundError, ServerNotRespondingError } from '@/errors';
5-
6-
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
7-
const ABORT_MS = Number(process.env.NEXT_PUBLIC_ABORT_MS);
8-
9-
if (Number.isNaN(ABORT_MS)) {
10-
throw new EnvNotFoundError('ABORT_MS');
11-
}
12-
13-
if (!BASE_URL) {
14-
throw new EnvNotFoundError('BASE_URL');
15-
}
4+
import { ServerNotRespondingError } from '@/errors';
5+
import { env } from '@/constants';
166

177
type ErrorType = {
188
code: string;
@@ -37,7 +27,7 @@ const abortPolyfill = (ms: number) => {
3727
};
3828

3929
const fetch = returnFetch({
40-
baseUrl: BASE_URL,
30+
baseUrl: env.BASE_URL,
4131
headers: {
4232
Accept: 'application/json',
4333
'Content-Type': 'application/json',
@@ -76,8 +66,8 @@ export const instance = async <I, R>(
7666
: init?.headers,
7767
body: init?.body ? JSON.stringify(init.body) : undefined,
7868
signal: AbortSignal.timeout
79-
? AbortSignal.timeout(ABORT_MS)
80-
: abortPolyfill(ABORT_MS),
69+
? AbortSignal.timeout(Number(env.ABORT_MS))
70+
: abortPolyfill(Number(env.ABORT_MS)),
8171
credentials: 'include',
8272
cache: 'no-store',
8373
});
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import { Metadata } from 'next';
12
import { ArriveSoon } from '@/components';
23

4+
export const metadata: Metadata = {
5+
title: '통계 비교',
6+
};
7+
38
export default function Page() {
49
return <ArriveSoon />;
510
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Header } from '@/components';
44
import { PATHS } from '@/constants';
55
import { me } from '@/apis';
66
import { getQueryClient } from '@/utils/queryUtil';
7-
import { Header } from '@/components';
87

98
interface IProp {
109
children: ReactElement;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { Metadata } from 'next';
12
import { ArriveSoon } from '@/components';
23
// import { Content } from './Content';
34

5+
export const metadata: Metadata = {
6+
title: '리더보드',
7+
};
8+
49
export default function Page() {
510
return <ArriveSoon />;
611
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Content } from './Content';
77

88
export const metadata: Metadata = {
99
title: '대시보드',
10-
description: '각종 Velog 통계를 볼 수 있는 대시보드',
1110
};
1211

1312
interface IProp {

src/app/(with-tracker)/(login)/page.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Content } from './Content';
44

55
export const metadata: Metadata = {
66
title: '로그인',
7-
description: '대시보드 페이지에 진입하기 전 표시되는 로그인 페이지',
87
};
98

109
export default function Page() {

src/app/layout.tsx

+18-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,29 @@ import 'react-toastify/dist/ReactToastify.css';
44
import * as sentry from '@sentry/nextjs';
55
import type { Metadata } from 'next';
66
import { ReactNode } from 'react';
7+
import { GoogleAnalytics } from '@next/third-parties/google';
78
import './globals.css';
89
import { ChannelTalkProvider, QueryProvider } from '@/components';
10+
import { env } from '@/constants';
11+
12+
export const BASE = 'https://velog-dashboard.kro.kr/';
913

1014
export const metadata: Metadata = {
1115
title: 'Velog Dashboard',
12-
description: 'Velog 통계를 확인할 수 있는 Velog Dashboard',
16+
metadataBase: new URL(BASE),
17+
description: '어디서든 편리하게 확인하는 Velog 통계 서비스, Velog Dashboard',
1318
icons: { icon: '/favicon.png' },
19+
alternates: {
20+
canonical: BASE,
21+
},
22+
openGraph: {
23+
siteName: 'Velog Dashboard',
24+
description:
25+
'어디서든 편리하게 확인하는 Velog 통계 서비스, Velog Dashboard',
26+
url: BASE,
27+
images: [{ url: '/opengraph-image.png', alt: 'Velog Dashboard' }],
28+
type: 'website',
29+
},
1430
};
1531

1632
const NotoSansKr = Noto_Sans_KR({ subsets: ['latin'] });
@@ -30,6 +46,7 @@ export default function RootLayout({
3046
</QueryProvider>
3147
</sentry.ErrorBoundary>
3248
</body>
49+
<GoogleAnalytics gaId={env.GA_ID} />
3350
</html>
3451
);
3552
}

src/app/robots.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { MetadataRoute } from 'next';
2+
import { BASE } from './layout';
3+
4+
export default function robots(): MetadataRoute.Robots {
5+
return {
6+
rules: {
7+
userAgent: '*',
8+
allow: '/',
9+
},
10+
sitemap: BASE + '/sitemap.xml',
11+
};
12+
}

src/app/sitemap.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { MetadataRoute } from 'next';
2+
import { BASE } from './layout';
3+
4+
export default function sitemap(): MetadataRoute.Sitemap {
5+
return [
6+
{ url: BASE, lastModified: new Date(), changeFrequency: 'monthly' },
7+
{
8+
url: BASE + '/main',
9+
lastModified: new Date(),
10+
changeFrequency: 'monthly',
11+
},
12+
{
13+
url: BASE + '/leaderboards',
14+
lastModified: new Date(),
15+
changeFrequency: 'monthly',
16+
},
17+
{
18+
url: BASE + '/compare',
19+
lastModified: new Date(),
20+
changeFrequency: 'monthly',
21+
},
22+
];
23+
}

src/components/auth-required/header/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const Header = () => {
4545
queryKey: [PATHS.ME],
4646
queryFn: me,
4747
});
48-
48+
4949
useEffect(() => {
5050
const handleClickOutside = (e: MouseEvent) =>
5151
open &&

0 commit comments

Comments
 (0)