diff --git a/.gitignore b/.gitignore
index b059b28..47e9438 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,4 @@ next-env.d.ts
.env
# Sentry Config File
.env.sentry-build-plugin
-todo
+todo
\ No newline at end of file
diff --git a/cypress.config.ts b/cypress.config.ts
index 679438c..f07841f 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -2,6 +2,8 @@ import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
+ chromeWebSecurity: false, // Cross-Origin 제한 해제
+
setupNodeEvents(on, config) {
// implement node event listeners here
},
diff --git a/cypress/e2e/home.cy.ts b/cypress/e2e/home.cy.ts
deleted file mode 100644
index b61b444..0000000
--- a/cypress/e2e/home.cy.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export {};
-
-describe('홈페이지 테스트', () => {
- beforeEach(() => {
- cy.visit('/');
- });
-});
diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts
new file mode 100644
index 0000000..a233b02
--- /dev/null
+++ b/cypress/e2e/login.cy.ts
@@ -0,0 +1,10 @@
+export function logintest() {
+ cy.visit('/sign');
+ cy.get('input[type="email"]').type('moaguide1');
+ cy.get('input[type="password"]').type('qwer1234!');
+ cy.get('.submit').click();
+
+ // 로그인 성공 후 세션 유지 확인
+ cy.url().should('not.include', '/sign');
+ // cy.getCookie('access_token').should('exist');
+}
diff --git a/cypress/e2e/paymentIndex.cy.ts b/cypress/e2e/paymentIndex.cy.ts
new file mode 100644
index 0000000..cb43f65
--- /dev/null
+++ b/cypress/e2e/paymentIndex.cy.ts
@@ -0,0 +1,42 @@
+describe('PaymentIndex Component', () => {
+ beforeEach(() => {
+ cy.intercept('GET', '/api/payment-status', { fixture: 'paymentStatus.json' }).as(
+ 'getPaymentStatus'
+ );
+ cy.visit('/payment');
+ });
+
+ it('renders payment benefits correctly', () => {
+ // Check that the header and subscription benefits are rendered
+ cy.get('.text-heading2').contains('구독 시작하기');
+ cy.get('.text-body7').should('have.length', 4);
+ });
+
+ it('allows selecting a subscription option', () => {
+ // Click on the first subscription option and check that it is selected
+ cy.get('[data-testid="subscription-option"]').first().click();
+ cy.get('[data-testid="subscription-option"]')
+ .first()
+ .should('have.class', 'border-normal');
+ });
+
+ it('redirects to the correct page based on login status', () => {
+ // Mock logged-in state
+ cy.setCookie('access_token', 'validToken');
+ cy.get('.cta-button').click();
+ cy.url().should('include', '/payment/check');
+
+ // Mock logged-out state
+ cy.clearCookie('access_token');
+ cy.get('.cta-button').click();
+ cy.url().should('include', '/sign');
+ });
+
+ it('handles back button click', () => {
+ // Check that clicking the back button navigates to the previous page
+ cy.get('.back-button').click();
+ cy.url().should('not.include', '/payment');
+ });
+});
+
+export {};
diff --git a/cypress/e2e/paymentTest.cy.ts/paymentcycle.cy.ts b/cypress/e2e/paymentTest.cy.ts/paymentcycle.cy.ts
new file mode 100644
index 0000000..633be84
--- /dev/null
+++ b/cypress/e2e/paymentTest.cy.ts/paymentcycle.cy.ts
@@ -0,0 +1,50 @@
+/* eslint-disable */
+import { logintest } from '../login.cy';
+
+describe('Full Payment Flow', () => {
+ beforeEach(() => {
+ cy.intercept('GET', '/api/payment-status', { fixture: 'paymentStatus.json' }).as(
+ 'getPaymentStatus'
+ );
+ cy.intercept('GET', '/api/issubscribed', { subscribed: false }).as('getIsSubscribed');
+ // cy.setCookie('access_token', 'your-mock-token');
+ });
+
+ it('completes a full payment flow', () => {
+ logintest();
+ cy.visit('/payment');
+ cy.wait(3000);
+ cy.contains('div', '첫 달 무료체험하기').should('exist').click();
+ cy.url().should('include', '/payment/check');
+ cy.get('.subscribed_check').click();
+ cy.wait(1500);
+ cy.get('.payment_start').click();
+ cy.wait(3000);
+
+ //토스 페이먼츠 결제 테스트
+
+ cy.get('#__tosspayments_payment-gateway_iframe__')
+ // .should('exist')
+ .then(($iframe) => {
+ const body = $iframe.contents().find('body');
+ cy.log(body.html());
+ cy.wrap(body)
+ .should('not.be.empty')
+ .within(() => {
+ cy.get('input[aria-label="카드번호 1 ~ 4 자리"]').type('6243');
+ cy.get('input[aria-label="카드번호 5 ~ 8 자리"]').type('6303');
+ cy.get('input[aria-label="카드번호 9 ~ 12 자리"]').type('1763');
+ cy.get('input[aria-label="카드번호 13 ~ 16 자리"]').type('5652');
+ cy.get('input[aria-label="카드 유효기간"]').type('0825');
+ cy.get('input[aria-label="주민등록번호 생년월일"]').type('010310');
+ cy.get('input[aria-label="주민등록번호 성별"]').type('3');
+ cy.get('input[type="checkbox"]').check();
+ cy.contains('button', '다음').click();
+ });
+ });
+
+ cy.url().should('include', '/payment/check/confirm/successloading');
+ cy.get('p').contains('결제가 진행중입니다...').should('exist');
+ });
+});
+export {};
diff --git a/cypress/fixtures/isSubscribed.json b/cypress/fixtures/isSubscribed.json
new file mode 100644
index 0000000..6afe34f
--- /dev/null
+++ b/cypress/fixtures/isSubscribed.json
@@ -0,0 +1,3 @@
+{
+ "subscribed": true
+}
diff --git a/cypress/fixtures/paymentStatus.json b/cypress/fixtures/paymentStatus.json
new file mode 100644
index 0000000..15e2ebf
--- /dev/null
+++ b/cypress/fixtures/paymentStatus.json
@@ -0,0 +1,4 @@
+{
+ "status": "success",
+ "message": "Payment status retrieved successfully"
+}
diff --git a/next.config.mjs b/next.config.mjs
index b1f02ba..87d9ca6 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -8,7 +8,6 @@ const bundleAnalyzer = withBundleAnalyzer({
});
const nextConfig = {
reactStrictMode: false,
- styledComponents: true,
output: 'standalone',
experimental: { instrumentationHook: true },
@@ -47,42 +46,20 @@ const nextConfig = {
}
};
-export default withSentryConfig(bundleAnalyzer(nextConfig), {
- // For all available options, see:
- // https://github.com/getsentry/sentry-webpack-plugin#options
-
+const SentryWebpackPluginOptions = {
org: 'moaguide',
project: 'javascript-nextjs',
-
- // Only print logs for uploading source maps in CI
+ authToken: process.env.SENTRY_AUTH_TOKEN,
silent: !process.env.CI,
-
- // For all available options, see:
- // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
-
- // Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
-
- // Automatically annotate React components to show their full name in breadcrumbs and session replay
+ sourcemaps: {
+ deleteSourcemapsAfterUpload: true
+ },
reactComponentAnnotation: {
enabled: true
},
-
- // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
- // This can increase your server load as well as your hosting bill.
- // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
- // side errors will fail.
- tunnelRoute: '/monitoring',
-
- // Hides source maps from generated client bundles
- hideSourceMaps: true,
-
- // Automatically tree-shake Sentry logger statements to reduce bundle size
+ hideSourceMaps: false,
disableLogger: true,
-
- // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
- // See the following for more information:
- // https://docs.sentry.io/product/crons/
- // https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true
-});
+};
+export default withSentryConfig(bundleAnalyzer(nextConfig), SentryWebpackPluginOptions);
diff --git a/package.json b/package.json
index d6e0cc6..345afb6 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"start": "next start -p 80",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"analyze": "cross-env ANALYZE=true next build",
- "cypress:open": "cypress open"
+ "cypress": "cypress open"
},
"dependencies": {
"@sentry/nextjs": "8",
diff --git a/public/images/learning/articleLiked.svg b/public/images/learning/articleLiked.svg
new file mode 100644
index 0000000..6ddd987
--- /dev/null
+++ b/public/images/learning/articleLiked.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/learning/articleShare.svg b/public/images/learning/articleShare.svg
new file mode 100644
index 0000000..3f4157a
--- /dev/null
+++ b/public/images/learning/articleShare.svg
@@ -0,0 +1,8 @@
+
diff --git a/sentry.properties b/sentry.properties
deleted file mode 100644
index 88ae701..0000000
--- a/sentry.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-defaults.url=https://sentry.io/
-defaults.org=YOUR_ORGANIZATION
-defaults.project=YOUR_PROJECT_NAME
-
-auth.token=YOUR_AUTH_TOKEN
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 030d3d8..8e53441 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -14,13 +14,13 @@ import NaverAnalytics from '@/lib/NaverAnalytics';
import AuthWrapper from '@/components/common/AuthWrapper';
import ToastProvider from '@/providers/ToastProvider';
import RefreshTokenWrapper from '@/components/common/RefreshTokenWrapper';
+import * as Sentry from '@sentry/nextjs';
declare global {
interface Window {
kakao: any;
}
}
-
const pretendard = localFont({
src: '../static/fonts/PretendardVariable.woff2',
display: 'swap',
@@ -55,6 +55,11 @@ export const metadata: Metadata = {
}
};
+Sentry.init({
+ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
+ tracesSampleRate: 1.0 // 조정 가능
+});
+
export default function RootLayout({
children
}: Readonly<{
diff --git a/src/app/learning/detail/[articleId]/page.tsx b/src/app/learning/detail/[articleId]/page.tsx
new file mode 100644
index 0000000..0961eea
--- /dev/null
+++ b/src/app/learning/detail/[articleId]/page.tsx
@@ -0,0 +1,17 @@
+import Navbar from '@/components/common/Navbar';
+import ArticleDetailClientWrapper from '@/components/learning/article/ArticleDetailClientWrapper';
+
+interface PageProps {
+ params: { articleId: string };
+}
+
+export default function ArticleDetailPage({ params }: PageProps) {
+ const articleId = params.articleId;
+
+ return (
+ <>
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/src/app/mypage/cardmanagement/success/page.tsx b/src/app/mypage/cardmanagement/success/page.tsx
index ab89bf2..db1b177 100644
--- a/src/app/mypage/cardmanagement/success/page.tsx
+++ b/src/app/mypage/cardmanagement/success/page.tsx
@@ -18,11 +18,11 @@ export default function CardRegisterSuccess() {
카드 등록에 성공했습니다.
- 등록 내역
+ {/* 등록 내역
주문 ID: {orderId || ''}
-
+ */}
@@ -74,7 +76,7 @@ const PaymentIndex = () => {
- 첫 달 무료체험하기
+ {isfirst ? '첫 달 무료 체험하기' : '4,900원 결제하기'}
);
diff --git a/src/app/payment/(payment)/TossPaymentsCardWidget.tsx b/src/app/payment/(payment)/TossPaymentsCardWidget.tsx
index eb0c2a8..c55f099 100644
--- a/src/app/payment/(payment)/TossPaymentsCardWidget.tsx
+++ b/src/app/payment/(payment)/TossPaymentsCardWidget.tsx
@@ -33,7 +33,7 @@ const TossPaymentsCardWidget = () => {
try {
await payment?.requestBillingAuth({
- method: 'CARD', // 자동결제(빌링)는 카드만 지원합니다
+ method: 'CARD',
successUrl: window.location.origin + `/payment/check/confirm/successloading`,
failUrl: window.location.origin + '/payment/check/confirm/fail',
diff --git a/src/app/payment/check/confirm/successloading/page.tsx b/src/app/payment/check/confirm/successloading/page.tsx
index 31a8a00..b229de8 100644
--- a/src/app/payment/check/confirm/successloading/page.tsx
+++ b/src/app/payment/check/confirm/successloading/page.tsx
@@ -43,6 +43,7 @@ const PaymentSuccessLoading = () => {
throw new Error('추가 작업에 실패했습니다.');
}
};
+
const mutation = useMutation({
mutationFn: fetchPayment,
retry: 0, // 재시도 비활성화
@@ -52,7 +53,7 @@ const PaymentSuccessLoading = () => {
fetchNextAPI()
.then((response) => {
console.log('연쇄 호출 성공:', response);
- router.push('/payment/check/confirm/success'); // 성공 시 페이지 이동
+ router.push(`/payment/check/confirm/success?orderId=${response?.orderId}`); // 성공 시 페이지 이동
})
.catch((error) => {
console.error('연쇄 호출 실패:', error);
@@ -67,6 +68,7 @@ const PaymentSuccessLoading = () => {
console.log('결제 요청 완료');
}
});
+
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
if (!data?.cardName) {
@@ -77,7 +79,7 @@ const PaymentSuccessLoading = () => {
fetchNextAPI()
.then((response) => {
console.log('연쇄 호출 성공:', response);
- router.push('/payment/check/confirm/success'); // 성공 시 페이지 이동
+ router.push(`/payment/check/confirm/success?orderId=${response?.orderId}`); // 성공 시 페이지 이동
})
.catch((error) => {
console.error('연쇄 호출 실패:', error);
diff --git a/src/app/payment/check/page.tsx b/src/app/payment/check/page.tsx
index 2d623bc..3bb3ca1 100644
--- a/src/app/payment/check/page.tsx
+++ b/src/app/payment/check/page.tsx
@@ -7,20 +7,21 @@ import { Line } from '@/components/common/Line';
import { getCoupon } from '@/factory/Coupon/getCoupon';
import Image from 'next/image';
import { useCheckCardRegister } from '@/factory/Card/CheckCardRegister';
+import { SubscribedStatus } from '@/utils/subscribedStatus';
const PaymentCheckPage = () => {
const { data } = getCoupon();
const couponLength = data?.coupons?.length as number;
const couponName = data?.coupons[0]?.couponName;
- console.log(data);
+
const router = useRouter();
const [isChecked, setIsChecked] = useState(false);
const { setModalType, setOpen } = useModalStore();
-
const { data: CheckCard, isLoading } = useCheckCardRegister();
-
const { requestBillingAuth } = TossPaymentsCardWidget();
+ const { Subscribestatus } = SubscribedStatus();
+ console.log(Subscribestatus);
const bililngRequest = () => {
if (isChecked) {
if (!CheckCard?.cardName) {
@@ -41,17 +42,16 @@ const PaymentCheckPage = () => {
결제하기
구독 플랜
-
- 1개월 구독 + 1개월
-
+
1개월 구독
쿠폰 사용
- {couponLength > 0 ? (
+ {isLoading ? null : couponLength > 0 ? (
{couponName}
@@ -61,7 +61,7 @@ const PaymentCheckPage = () => {
등록된 쿠폰이 없습니다.
{
router.push('/mypage/coupon');
}}>
@@ -79,21 +79,35 @@ const PaymentCheckPage = () => {
최종 결제 금액
-
0원
+
+ {isLoading ? null : couponLength > 0 ? (
+
0 원
+ ) : (
+
4,900 원
+ )}
-
-
- setIsChecked((prev) => !prev)}
- className="cursor-pointer"
- />
-
-
setIsChecked((prev) => !prev)}
- className="text-body8 cursor-pointer">
- 거래 내용을 확인하였으며, 동의합니다
+
+
+
+
+ setIsChecked((prev) => !prev)}
+ className="subscribed_check cursor-pointer"
+ />
+
+
setIsChecked((prev) => !prev)}
+ className="text-body8 cursor-pointer">
+ 거래 내용을 확인하였으며, 동의합니다
+
+
+ {Subscribestatus === 'unsubscribing' ? (
+
+ 실 결제는 구독만료일에 결제 됩니다.
+
+ ) : null}
@@ -103,10 +117,10 @@ const PaymentCheckPage = () => {
// isChecked && requestBillingAuth();
bililngRequest();
}}
- className={` my-10 py-[18px] w-full rounded-[12px] flex items-center justify-center text-title1
+ className={`payment_start my-10 py-[18px] w-full rounded-[12px] flex items-center justify-center text-title1
${isChecked ? 'cursor-pointer bg-gradient2 text-white' : 'bg-gray100 text-gray300'}
`}>
- 0원 결제하기
+ {couponLength > 0 ?
0원 결제하기
:
4,900 원 결제하기
}
);
diff --git a/src/app/product/detail/art/[id]/page.tsx b/src/app/product/detail/art/[id]/page.tsx
index ff23a3f..37a677e 100644
--- a/src/app/product/detail/art/[id]/page.tsx
+++ b/src/app/product/detail/art/[id]/page.tsx
@@ -23,6 +23,13 @@ const ArtDetailpage = (props: { params: { id: string } }) => {
const [localData, setLocalData] = useState(data);
const { handleBookmarkClick } = BookmarkUpdate({ data, localData, setLocalData });
+ const sortComponents: { [key: string]: JSX.Element } = {
+ news: ,
+ report: ,
+ profit: ,
+ detail:
+ };
+
return (
@@ -33,17 +40,7 @@ const ArtDetailpage = (props: { params: { id: string } }) => {
/>
-
- {sort === 'news' ? (
-
- ) : sort === 'report' ? (
-
- ) : sort === 'profit' ? (
-
- ) : sort === 'detail' ? (
-
- ) : undefined}
-
+
{sortComponents[sort]}
);
};
diff --git a/src/app/product/detail/building/[id]/page.tsx b/src/app/product/detail/building/[id]/page.tsx
index 1beae8a..0ab24e5 100644
--- a/src/app/product/detail/building/[id]/page.tsx
+++ b/src/app/product/detail/building/[id]/page.tsx
@@ -27,6 +27,20 @@ const BuildingDetailpage = (props: { params: { id: string } }) => {
const [localData, setLocalData] = useState(data);
const { handleBookmarkClick } = BookmarkUpdate({ data, localData, setLocalData });
+ const sortComponents: { [key: string]: JSX.Element } = {
+ public: ,
+ news: ,
+ report: ,
+ profit: ,
+ detail: (
+
+ )
+ };
+
return (
@@ -37,23 +51,8 @@ const BuildingDetailpage = (props: { params: { id: string } }) => {
/>
-
- {sort === 'public' ? (
-
- ) : sort === 'news' ? (
-
- ) : sort === 'report' ? (
-
- ) : sort === 'profit' ? (
-
- ) : sort === 'detail' ? (
-
- ) : undefined}
-
+
+ {sortComponents[sort]}
);
};
diff --git a/src/app/product/detail/content/[id]/page.tsx b/src/app/product/detail/content/[id]/page.tsx
index 5d1c5bf..90dcaa2 100644
--- a/src/app/product/detail/content/[id]/page.tsx
+++ b/src/app/product/detail/content/[id]/page.tsx
@@ -26,6 +26,21 @@ const ContentDetailpage = (props: { params: { id: string } }) => {
const { handleBookmarkClick } = BookmarkUpdate({ data, localData, setLocalData });
+ const sortComponents: { [key: string]: JSX.Element } = {
+ news: ,
+ report: ,
+ profit: (
+
+ ),
+ detail: (
+
+ )
+ };
+
return (
@@ -36,25 +51,8 @@ const ContentDetailpage = (props: { params: { id: string } }) => {
/>
-
- {sort === 'news' ? (
-
- ) : sort === 'report' ? (
-
- ) : sort === 'profit' ? (
-
- ) : sort === 'detail' ? (
-
- ) : undefined}
-
+
+ {sortComponents[sort]}
);
};
diff --git a/src/app/product/detail/cow/[id]/page.tsx b/src/app/product/detail/cow/[id]/page.tsx
index c11787b..f61aaca 100644
--- a/src/app/product/detail/cow/[id]/page.tsx
+++ b/src/app/product/detail/cow/[id]/page.tsx
@@ -25,6 +25,13 @@ const CowDetailpage = (props: { params: { id: string } }) => {
const [localData, setLocalData] = useState(data);
const { handleBookmarkClick } = BookmarkUpdate({ data, localData, setLocalData });
+ const sortComponents: { [key: string]: JSX.Element } = {
+ news: ,
+ report: ,
+ profit: ,
+ detail:
+ };
+
return (
@@ -35,17 +42,7 @@ const CowDetailpage = (props: { params: { id: string } }) => {
/>
-
- {sort === 'news' ? (
-
- ) : sort === 'report' ? (
-
- ) : sort === 'profit' ? (
-
- ) : sort === 'detail' ? (
-
- ) : undefined}
-
+ {sortComponents[sort]}
);
};
diff --git a/src/app/product/detail/music/[id]/page.tsx b/src/app/product/detail/music/[id]/page.tsx
index 3ea45d0..ff5c45c 100644
--- a/src/app/product/detail/music/[id]/page.tsx
+++ b/src/app/product/detail/music/[id]/page.tsx
@@ -25,6 +25,13 @@ const MusicDetailpage = (props: { params: { id: string } }) => {
const [localData, setLocalData] = useState(data);
const { handleBookmarkClick } = BookmarkUpdate({ data, localData, setLocalData });
+ const sortComponents: { [key: string]: JSX.Element } = {
+ news: ,
+ report: ,
+ profit: ,
+ detail:
+ };
+
return (
@@ -35,18 +42,7 @@ const MusicDetailpage = (props: { params: { id: string } }) => {
/>
-
-
- {sort === 'news' ? (
-
- ) : sort === 'report' ? (
-
- ) : sort === 'profit' ? (
-
- ) : sort === 'detail' ? (
-
- ) : undefined}
-
+ {sortComponents[sort]}
);
};
diff --git a/src/app/product/page.tsx b/src/app/product/page.tsx
index 5557236..d8942d1 100644
--- a/src/app/product/page.tsx
+++ b/src/app/product/page.tsx
@@ -1,7 +1,9 @@
import Navbar from '@/components/common/Navbar';
import Product from './(product)/Product';
import { IProductCommon, IReport, ISummaryData } from '@/types/Diviend';
-import { cookies } from 'next/headers';
+import { getCookie } from '@/utils/serverCookies';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'https://api.moaguide.com';
const ProductPage = async ({
searchParams
@@ -9,27 +11,22 @@ const ProductPage = async ({
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
}) => {
- const getCookie = (key: string) => {
- return cookies().get(key)?.value;
- };
const token = getCookie('access_token') || '';
const pages = searchParams['page'] || 1;
const subcategory = searchParams['subcategory'] || 'trade';
const sort = searchParams['sort'] || 'lastDivide_rate desc';
const category = searchParams['category'] || 'all';
- const buildingDiviedResponse = await fetch(`https://api.moaguide.com/summary`, {
+
+ const buildingDiviedResponse = await fetch(`${API_BASE_URL}/summary`, {
cache: 'no-store'
});
- const buildingReportResponse = await fetch(
- 'https://api.moaguide.com/summary/report/building',
- {
- cache: 'no-store'
- }
- );
+ const buildingReportResponse = await fetch(`${API_BASE_URL}/summary/report/building`, {
+ cache: 'no-store'
+ });
const productDetailResponse = await fetch(
- `https://api.moaguide.com/summary/list?category=${category}&subcategory=${subcategory}&sort=${sort}&page=${pages}&size=10`,
+ `${API_BASE_URL}/summary/list?category=${category}&subcategory=${subcategory}&sort=${sort}&page=${pages}&size=10`,
{
headers: {
Authorization: `Bearer ${token} `
diff --git a/src/app/sign/(sign)/SignLayout.tsx b/src/app/sign/(sign)/SignLayout.tsx
index 202a526..298f49b 100644
--- a/src/app/sign/(sign)/SignLayout.tsx
+++ b/src/app/sign/(sign)/SignLayout.tsx
@@ -109,7 +109,7 @@ const SignLayout = () => {
{errorMessage}
)}
-
+
{
const handleSubmit = async () => {
try {
const verifyToken = getCookie('verify_token');
-
if (!verifyToken) {
throw new Error('Verify token이 없습니다.');
}
-
const authHeaders = {
cookie: '',
Verify: verifyToken
};
-
const response = await finalSignup(formData, authHeaders);
-
if (response === '회원가입 완료') {
setModalType('signupComplete');
setOpen(true);
+ signupHistory(formData.email || '');
}
} catch (error) {
console.error('서버 요청 오류:', error);
diff --git a/src/components/common/GnbWrapper.tsx b/src/components/common/GnbWrapper.tsx
index eed8276..75a378e 100644
--- a/src/components/common/GnbWrapper.tsx
+++ b/src/components/common/GnbWrapper.tsx
@@ -9,7 +9,7 @@ const GnbWrapper = () => {
const [isGnbHidden, setIsGnbHidden] = useState(false);
useEffect(() => {
const checkIfGnbShouldBeHidden = () => {
- const pathsToHideGnb = ['/signup', '/sign', '/find', '/detail', '/login', '/quiz'];
+ const pathsToHideGnb = ['/signup', '/sign', '/find', '/login', '/quiz'];
const shouldHideGnb = pathsToHideGnb.some((path) => pathname.includes(path));
setIsGnbHidden(shouldHideGnb);
};
diff --git a/src/components/common/Navbar.tsx b/src/components/common/Navbar.tsx
index bb624fc..9cd4d18 100644
--- a/src/components/common/Navbar.tsx
+++ b/src/components/common/Navbar.tsx
@@ -7,7 +7,7 @@ const Navbar = () => {
const router = useRouter();
const pathname = usePathname();
return (
-
+
{
@@ -41,7 +41,7 @@ const Navbar = () => {
router.push('/practicepage');
}}
className={` desk:whitespace-nowrap px-4 py-3 flex-1 flex justify-center items-center cursor-pointer text-body5 desk2:text-heading4
- ${pathname === '/practicepage' ? 'text-black border-b-[2px] border-black' : 'text-gray300'}
+ ${pathname.startsWith('/learning') ? 'text-black border-b-[2px] border-black' : 'text-gray300'}
`}>
학습하기
diff --git a/src/components/learning/FilteredContents.tsx b/src/components/learning/FilteredContents.tsx
index 414b8a1..c2c8226 100644
--- a/src/components/learning/FilteredContents.tsx
+++ b/src/components/learning/FilteredContents.tsx
@@ -1,8 +1,11 @@
+import { useRouter } from 'next/navigation';
import Image from 'next/image';
import defaultImage from '../../../public/images/learning/learning_img.svg';
+import { FilteredResponse } from '@/types/filterArticle';
+import { getValidImageSrc } from '@/utils/checkImageProperty';
interface FilteredContentsProps {
- contents: any[];
+ contents: FilteredResponse['content'];
total: number;
page: number;
size: number;
@@ -16,23 +19,28 @@ const FilteredContents = ({
size,
onPageChange,
}: FilteredContentsProps) => {
+ const router = useRouter();
const totalPages = Math.ceil(total / size);
const formatDate = (dateString: string) => {
const date = new Date(dateString);
- return date.toISOString().split('T')[0];
+ return date.toISOString().split('T')[0];
};
return (
-
+
{contents.length > 0 ? (
contents.map((item) => (
-
+
router.push(`/learning/detail/${item.article.articleId}`)}
+ >
-
- {item.title}
+ {item.article.title}
- {item.description}
+ {item.article.description || '설명 없음'}
- {formatDate(item.date)}
- ❤ {item.likes}
- 👁 {item.views}
+ {formatDate(item.article.date)}
+ ❤ {item.article.likes}
+ 👁 {item.article.views}
@@ -58,7 +66,6 @@ const FilteredContents = ({
)}
- {/* 페이지네이션 */}
{totalPages > 1 && (
diff --git a/src/components/learning/LatestNewsClipping.tsx b/src/components/learning/LatestNewsClipping.tsx
index 27ad164..1f03213 100644
--- a/src/components/learning/LatestNewsClipping.tsx
+++ b/src/components/learning/LatestNewsClipping.tsx
@@ -4,8 +4,10 @@ import React, { useEffect, useState } from 'react';
import Image from 'next/image';
import defaultImage from '../../../public/images/learning/learning_img.svg';
import LatestNewsClippingSkeleton from '../skeleton/LatestNewsClippingSkeleton';
+import { useRouter } from 'next/navigation';
const LatestNewsClipping = ({ contents }: { contents: any[] }) => {
+ const router = useRouter();
const [isMobile, setIsMobile] = useState(null);
useEffect(() => {
@@ -34,7 +36,7 @@ const LatestNewsClipping = ({ contents }: { contents: any[] }) => {
// 모바일 레이아웃
{contents[0] && (
-
+
router.push(`/learning/detail/${contents[0].articleId}`)}>
{
)}
{contents.slice(1, 5).map((content, index) => (
-
+
router.push(`/learning/detail/${content.articleId}`)}>
{
// 데스크톱 레이아웃
{contents[0] && (
-
+
router.push(`/learning/detail/${contents[0].articleId}`)}>
{
)}
{contents.slice(1, 5).map((content, index) => (
-
+
router.push(`/learning/detail/${content.articleId}`)}>
{
+const LearningPageClient = ({ initialData }: { initialData: OverviewResponse }) => {
const [selectedType, setSelectedType] = useState('');
const [selectedCategory, setSelectedCategory] = useState('');
const [activeDropdown, setActiveDropdown] = useState(null);
const [page, setPage] = useState(1);
-
- const fetchContentsWithPage = async () => {
- const type = selectedType || 'all';
- const category = selectedCategory || 'all';
- const endpoint = `http://43.200.90.72/contents/list?type=${type}&category=${category}&page=${page}`;
- const response = await fetch(endpoint);
- if (!response.ok) throw new Error('API 호출 실패');
- return response.json();
- };
-
+
const { data, isLoading } = useQuery({
queryKey: ['contents', selectedType, selectedCategory, page],
queryFn: fetchContentsWithPage,
@@ -57,6 +50,14 @@ const LearningPageClient = ({ initialData }: { initialData: any }) => {
setActiveDropdown(null);
};
+ const extractContents = (contents: Content[] | undefined) =>
+ Array.isArray(contents)
+ ? contents.map((item) => ({
+ ...item.article,
+ likedByMe: item.likedByMe,
+ }))
+ : [];
+
return (
@@ -67,8 +68,8 @@ const LearningPageClient = ({ initialData }: { initialData: any }) => {
objectFit="cover"
className="w-full"
/>
-
-