diff --git a/apps/realworld/src/app/login/page.tsx b/apps/realworld/src/app/login/page.tsx index 7ace16ee..f0ed9aae 100644 --- a/apps/realworld/src/app/login/page.tsx +++ b/apps/realworld/src/app/login/page.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import { PathBuilder } from '@/shared/utils/routes'; +import React from 'react'; const Login = () => { return ( @@ -9,7 +10,7 @@ const Login = () => {

Sign in

- Need an account? + Need an account?

diff --git a/apps/realworld/src/entities/article/ui/article-preview/article-preview.tsx b/apps/realworld/src/entities/article/ui/article-preview/article-preview.tsx index e44a298c..6a03f428 100644 --- a/apps/realworld/src/entities/article/ui/article-preview/article-preview.tsx +++ b/apps/realworld/src/entities/article/ui/article-preview/article-preview.tsx @@ -1,3 +1,4 @@ +import { PathBuilder } from '@/shared/utils/routes'; import Link from 'next/link'; import React from 'react'; @@ -9,7 +10,7 @@ interface ArticlePreviewProps { const ArticlePreview = ({ description, title, slug }: ArticlePreviewProps) => { return ( - +

{title}

{description}

diff --git a/apps/realworld/src/entities/article/ui/read-more-button/read-more-button.tsx b/apps/realworld/src/entities/article/ui/read-more-button/read-more-button.tsx index 1c64e5fb..deac9da0 100644 --- a/apps/realworld/src/entities/article/ui/read-more-button/read-more-button.tsx +++ b/apps/realworld/src/entities/article/ui/read-more-button/read-more-button.tsx @@ -1,3 +1,4 @@ +import { PathBuilder } from '@/shared/utils/routes'; import Link from 'next/link'; import React from 'react'; @@ -7,7 +8,7 @@ interface ReadMoreButtonProps { const ReadMoreButton = ({ slug }: ReadMoreButtonProps) => { return ( - + Read More.. ); diff --git a/apps/realworld/src/entities/comment/ui/induce-sign-in/induce-sign-in.tsx b/apps/realworld/src/entities/comment/ui/induce-sign-in/induce-sign-in.tsx index 32368d35..68bf7f27 100644 --- a/apps/realworld/src/entities/comment/ui/induce-sign-in/induce-sign-in.tsx +++ b/apps/realworld/src/entities/comment/ui/induce-sign-in/induce-sign-in.tsx @@ -1,14 +1,15 @@ +import { PathBuilder } from '@/shared/utils/routes'; import Link from 'next/link'; import React from 'react'; const InduceSignIn = () => { return (

- + Sign in {' '} or{` `} - + sign up {' '} to add comments on this article. diff --git a/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx b/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx index f452e457..68b491fb 100644 --- a/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx +++ b/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx @@ -1,3 +1,4 @@ +import { PathBuilder } from '@/shared/utils/routes'; import { Tag } from '@packages/ui'; import Link from 'next/link'; import React from 'react'; @@ -9,7 +10,7 @@ interface ArticleTagListProps { const ArticleTagList = ({ tagList, slug }: ArticleTagListProps) => { if (slug) { return ( - + {tagList.map(label => ( ))} diff --git a/apps/realworld/src/features/article/ui/article-list-pagination/article-list-pagination.tsx b/apps/realworld/src/features/article/ui/article-list-pagination/article-list-pagination.tsx index 2a4db979..902fd6c0 100644 --- a/apps/realworld/src/features/article/ui/article-list-pagination/article-list-pagination.tsx +++ b/apps/realworld/src/features/article/ui/article-list-pagination/article-list-pagination.tsx @@ -1,6 +1,7 @@ 'use client'; import { generatePageList, getItemActivation } from '@/entities/article/api/page'; import { getItemIndex } from '@/shared/utils/array'; +import { PathBuilder } from '@/shared/utils/routes'; import { useRouter } from 'next/navigation'; import React from 'react'; @@ -12,7 +13,7 @@ interface ArticleListPaginationProps { const ArticleListPagination = ({ currentPage = 1, lastPage }: ArticleListPaginationProps) => { const { push } = useRouter(); const handleChangePage = (page: number) => { - push(`/?page=${page}`); + push(PathBuilder.buildHome().addPage(page).getPath()); }; const pageList = generatePageList(lastPage); diff --git a/apps/realworld/src/shared/utils/routes.test.ts b/apps/realworld/src/shared/utils/routes.test.ts new file mode 100644 index 00000000..6197b849 --- /dev/null +++ b/apps/realworld/src/shared/utils/routes.test.ts @@ -0,0 +1,63 @@ +import { PathBuilder } from './routes'; + +const context = describe; + +describe('PathBuilder에서', () => { + context('home path를 빌드할 때', () => { + it('/ 경로가 생성된다.', () => { + const homePath = PathBuilder.buildHome().getPath(); + + expect(homePath).toEqual('/'); + }); + + it('addPage로 경로가 생성된다.', () => { + const page = 2; + const homePathWithPageParam = PathBuilder.buildHome().addPage(page).getPath(); + + expect(homePathWithPageParam).toEqual(`/?page=${page}`); + }); + }); + + context('register path를 빌드할 때 ', () => { + it('/register 경로가 생성된다.', () => { + const buildedPath = PathBuilder.buildRegister().getPath(); + + expect(buildedPath).toEqual('/register'); + }); + }); + + context('login path를 빌드할 때 ', () => { + it('/login 경로가 생성된다.', () => { + const buildedPath = PathBuilder.buildLogin().getPath(); + + expect(buildedPath).toEqual('/login'); + }); + }); + + context('article path를 빌드할 때', () => { + it('addSlug로 경로가 생성된다.', () => { + const slug = 'this is slug'; + const path = PathBuilder.buildArticle().addSlug(slug).getPath(); + + expect(path).toEqual(`/article/${slug}`); + }); + + it('addSlug가 호출되지 않으면 에러가 발생한다.', () => { + try { + PathBuilder.buildArticle().getPath(); + expect(true).toBe(false); + } catch (e) { + expect(e).toBeInstanceOf(Error); + } + }); + + it('addSlug가 호출되지 않으면 에러가 발생한다.', () => { + try { + PathBuilder.buildArticle().addSlug('1').addSlug('2').getPath(); + expect(true).toBe(false); + } catch (e) { + expect(e).toBeInstanceOf(Error); + } + }); + }); +}); diff --git a/apps/realworld/src/shared/utils/routes.ts b/apps/realworld/src/shared/utils/routes.ts new file mode 100644 index 00000000..be6dbde3 --- /dev/null +++ b/apps/realworld/src/shared/utils/routes.ts @@ -0,0 +1,74 @@ +export class PathBuilder { + private path: string; + + private constructor(path: string) { + this.path = path; + } + + static buildHome(): HomePathBuilder { + return new HomePathBuilder('/'); + } + + static buildRegister(): PathBuilder { + return new PathBuilder('/register'); + } + + static buildLogin(): PathBuilder { + return new PathBuilder('/login'); + } + + static buildArticle(): ArticlePathBuilder { + return new ArticlePathBuilder('/article'); + } + getPath(): string { + return this.path; + } +} + +class HomePathBuilder { + private path: string; + private params: string; + constructor(path: '/') { + this.path = path; + this.params = '?'; + } + + private getParamsString() { + if (this.params === '?') { + return ''; + } + return this.params; + } + + addPage(page: number) { + this.params += `page=${page}`; + return this; + } + + getPath() { + return this.path + this.getParamsString(); + } +} + +class ArticlePathBuilder { + private path: string; + private count: number; + + constructor(path: '/article') { + this.path = path; + this.count = 0; + } + + addSlug(slug: string): ArticlePathBuilder { + this.path += `/${slug}`; + this.count += 1; + return this; + } + + getPath(): string { + if (this.count !== 1) { + throw new Error('article path must have slug or only once'); + } + return this.path; + } +} diff --git a/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx b/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx index e3a76dcd..639b9e04 100644 --- a/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx +++ b/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx @@ -1,6 +1,7 @@ 'use client'; import { responsiveWidth } from '@/shared/css/responsive-width'; +import { PathBuilder } from '@/shared/utils/routes'; import Link from 'next/link'; import React from 'react'; @@ -8,22 +9,22 @@ const GNB = () => { return (