๐ป Spaghetti Code Refactor
import { createBrowserRouter } from "react-router-dom" ;
import HomePage from "../pages/Home" ;
import PostListPage from "../pages/Post.List" ;
import PostDetailPage from "../pages/Post.detail" ;
export const router = createBrowserRouter ( [
{ path : "/" , element : < HomePage /> } ,
{ path : "/posts" , element : < PostListPage /> } ,
{ path : "/post-detail/:postId" , element : < PostDetailPage /> } ,
] ) ;
App.jsx์์ ๊ด๋ฆฌํ๊ณ ์๋ router ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ App.jsx์์ importํด์ ์ฌ์ฉํ์ต๋๋ค.
๐ ์ฌ์ฌ์ฉ๋๋ ๊ฐ์ consts๋ก
export const LIMIT_TAKE = 10 ;
export const LIMIT_PAGE = 10 ;
export const QUERY_KEY = {
post : "post" ,
posts : "posts" ,
comments : "comments" ,
weather : "weather" ,
} ;
LIMIT_PAGE, LIMIT_TAKE ๊ฐ์ pageNation์์ ์์ฃผ ์ฌ์ฌ์ฉ๋๋ ๊ฐ์ด๋ฏ๋ก consts ํด๋๋ก ๊ด๋ฆฌํ์ต๋๋ค.
react-query๋ฅผ ์ฌ์ฉํ์ฌ msw ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉ๋๋ query-key๊ฐ ๋ํ ์์ฃผ ์ฌ์ฌ์ฉ๋๋ฏ๋ก consts ํด๋๋ก ๊ด๋ฆฌํ์ต๋๋ค.
๐ msw ๋ฐ์ดํฐ ๊ด๋ฆฌ
import axios from "axios" ;
import { weatherConfig } from "../third-party/weather.config" ;
import { LIMIT_PAGE , LIMIT_TAKE } from "../consts/pageNation" ;
export const getPostDetailPost = async ( ) => {
const response = await axios . get ( "/api/post" ) ;
return response . data ;
} ;
export const getPaginationPost = async ( params ) => {
const response = await axios . get ( "/api/posts" , {
params : {
page : params . get ( "page" ) ?? 1 ,
take : params . get ( "take" ) ?? LIMIT_TAKE ,
limit : params . get ( "limit" ) ?? LIMIT_PAGE ,
} ,
} ) ;
return response . data ;
} ;
export const getPaginationComment = async ( params ) => {
const response = await axios . get ( "/api/comments" , {
params : {
page : params . get ( "page" ) ?? 1 ,
take : params . get ( "take" ) ?? LIMIT_TAKE ,
limit : params . get ( "limit" ) ?? LIMIT_PAGE ,
} ,
} ) ;
return response . data ;
} ;
export const getWeather = async ( ) => {
try {
const response = await axios . get ( "/getUltraSrtNcst" , {
baseURL : weatherConfig . api ,
params : {
serviceKey : weatherConfig . secret_key ,
dataType : "JSON" ,
base_date : new Date ( ) . toISOString ( ) . substring ( 0 , 10 ) . replace ( / - / g, "" ) ,
base_time : "0600" ,
nx : 60 ,
ny : 127 ,
} ,
} ) ;
return response . data ;
} catch ( err ) {
console . log ( err ) ;
throw new Error ( "failed load weather api" ) ;
}
} ;
components/pagenation/Pagenation.Comment.jsx
const { data : commentData } = useQuery ( [ QUERY_KEY . comments , params . get ( "page" ) ] , ( ) => getPaginationComment ( params ) ) ;
const paginationData = commentData ?. PageNation ;
components/pagenation/Pagenation.Post.jsx
const { data : postData } = useQuery ( [ QUERY_KEY . posts , params . get ( "page" ) ] , ( ) => getPaginationPost ( params ) ) ;
const pageNationData = postData ?. PageNation ;
const { data : weatherData } = useQuery ( [ QUERY_KEY . weather ] , ( ) => getWeather ( ) ) ;
const { data : postDetailData } = useQuery ( [ QUERY_KEY . post ] , ( ) => getPostDetailPost ( ) ) ;
const { data : commentData } = useQuery ( [ QUERY_KEY . comments , params . get ( "page" ) ] , ( ) => getPaginationComment ( params ) ) ;
const paginationCommentData = commentData ?. Comments ;
const { data : postData } = useQuery ( [ QUERY_KEY . posts , params . get ( "page" ) ] , ( ) => getPaginationPost ( params ) ) ;
const paginationPostData = postData ?. Posts ;
api์ ๊ด๋ จ๋ ๋ก์ง์ apiํด๋์์ ๊ด๋ฆฌํ์ต๋๋ค.
api ํธ์ถ ํจ์(Post ๋ก์ง)์ params ๊ฐ์ ์ ๋ฌํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๋ฉด {PageNation:,Posts:} ํ์์ผ๋ก ๋์ค๋๋ฐ ์ด๋ PageNation ๊ฐ์ pagenation ํด๋์์ Posts ๊ฐ์ pages ํด๋์์ ์ฌ์ฉํ์ต๋๋ค.
api ํธ์ถ ํจ์(Comment ๋ก์ง)์ params ๊ฐ์ ์ ๋ฌํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๋ฉด {PageNation:,Comments:} ํ์์ผ๋ก ๋์ค๋๋ฐ ์ด๋ PageNation ๊ฐ์ pagenation ํด๋์์ Comments ๊ฐ์ pages ํด๋์์ ์ฌ์ฉํ์ต๋๋ค.
๐ state๋ฅผ ์ด์ฉํ ์์ฃผ ์ฌ์ฉํ๋ ๋ก์ง์ hooks๋ก
import { DialLogState , useDiaLogStore } from "../contexts/DialogProvider" ;
const useDiaLog = ( ) => {
const [ , setDiaLogAttribute ] = useDiaLogStore ( ) ;
// home page
const onPressNavigateBlog = ( ) => {
setDiaLogAttribute ( {
type : DialLogState . ALERT ,
text : "์ ๋ง๋ก ํ์ด์ง๋ฅผ ์ด๋ํ๊ฒ ์ต๋๊น" ,
isOpen : true ,
onConfirm : async ( ) => {
await setDiaLogAttribute ( { isOpen : false } ) ;
window . location . href = "/posts" ;
} ,
} ) ;
} ;
// postList page
const onClickPost = async ( postId ) => {
await setDiaLogAttribute ( {
type : DialLogState . CONFIRM ,
text : "์ ๋ง๋ก ํ์ด์ง๋ฅผ ์ด๋ํ๊ฒ ์ต๋๊น" ,
isOpen : true ,
onConfirm : async ( ) => {
await setDiaLogAttribute ( {
text : "์ ๋ง๋ก ์ด๋ํด๋ฒ๋ฆฐ๋ค์!" ,
onConfirm : async ( ) => {
window . location . href = `/post-detail/${ postId } ` ;
} ,
} ) ;
} ,
onCancel : ( ) => {
setDiaLogAttribute ( { isOpen : false } ) ;
} ,
} ) ;
} ;
return { onPressNavigateBlog, onClickPost } ;
} ;
export default useDiaLog ;
import { useState } from "react" ;
const useShowModal = ( ) => {
const [ isOpenCommentList , setIsOpenCommentList ] = useState ( false ) ;
const onClickMoreComments = async ( ) => {
setIsOpenCommentList ( true ) ;
} ;
const onClickHiddenComments = ( ) => {
setIsOpenCommentList ( false ) ;
} ;
return { isOpenCommentList, onClickMoreComments, onClickHiddenComments } ;
} ;
export default useShowModal ;
context๋ฅผ ํตํด dialog์ ๊ด๋ จ๋ ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์์ง๋ง ์ด ์ํ๋ฅผ ์ฌ์ฉํ๋ ํจ์๋ ์ฌ๋ฌ ํ์ผ์์ ์ฌ์ฉ๋๊ณ ์์ต๋๋ค.
์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌ๋๊ณ ์๋ dialog์ ๊ด๋ จ๋ ์ํ๋ฅผ ์ฌ์ฉํ๋ ํจ์๋ฅผ useDiaLog custom hook์ผ๋ก ๋ง๋ค์ด ํ์ํ ๋ import ํด์ ์ฌ์ฉํ ์ ์๋๋ก ์ถ๊ฐํ์ต๋๋ค.
๋๊ธ์ฐฝ์ ์ด๊ณ ๋ซ์ ์ ์๋ ๋ก์ง์ธ useShowModal์ ํด๋น ์ฝ๋์์๋ ์ฌ์ฌ์ฉ๋๊ณ ์์ง ์์ง๋ง ๋จ์ํ ๋ชจ๋ฌ์ฐฝ์ ์ด๊ณ ๋ซ๋ ๋ก์ง์ ์์ฃผ ์ฌ์ฌ์ฉ๋ ์ ์๋ค๊ณ ์๊ฐํ์ฌ custom hook์ผ๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
๐ localStorage์ ๊ด๋ จ๋ ๋ก์ง์ ๋ฌถ์ด์
repository/userNameRepository.jsx
const USER_NAME = "useName" ;
export const userNameRepository = {
getUserName ( ) {
return localStorage . getItem ( USER_NAME ) ;
} ,
setUserName ( userName ) {
localStorage . setItem ( USER_NAME , userName ) ;
} ,
} ;
localStorage๋ฅผ ํตํด ๋ฐ์ดํฐ ๊ฐ์ ๊ฐ์ ธ์ค๋ ๋ก์ง์ด๋ localStorage์ ์๋ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๋ก์ง์ ์์ฃผ ์ฌ์ฌ์ฉ๋๋ฏ๋ก repository ํด๋์์ ํ๋์ ๊ฐ์ฒด๋ก ๊ด๋ฆฌํ์ต๋๋ค.
๐ private router์ ๊ด๋ จ๋ ๋ก์ง์ util ํจ์๋ก ๊ด๋ฆฌ
import { userNameRepository } from "../repository/userNameRepository" ;
const privateRouter = ( ) => {
const userName = userNameRepository . getUserName ( ) ;
if ( ! userName ) {
alert ( "๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค" ) ;
window . location . href = "/" ;
}
} ;
export default privateRouter ;
localStorage์ ๋ฐ์ดํฐ๊ฐ ์์๋ ํํ์ด์ง๋ก ๋๋์๊ฐ๋ private Router์ ๊ด๋ จ๋ ํจ์๋ ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ์๋ ๋ชจ๋ ํ์ด์ง์์ ์ฌ์ฌ์ฉ๋์ง๋ง state๋ฅผ ์ฌ์ฉํ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ utils ํด๋์์ ๊ด๋ฆฌํ์ต๋๋ค.
๐ style์ ํ ๊ณณ์์ ๊ด๋ฆฌ
import styled from "styled-components" ;
const BlurBackGround = styled . div `
position: fixed;
width: 100%;
height: 100vh;
z-index: 9999;
backdrop-filter: blur(10px);
` ;
const UserNameForm = styled . form `
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
` ;
const Button = styled . button `` ;
export const S = {
BlurBackGround,
UserNameForm,
Button,
} ;
style ๊ด๋ จ ๋ก์ง์ page์์ componets์์ ๊ฐ์ด ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ณ ๊ฐ๋
์ฑ์ด ๋จ์ด์ง๋ฏ๋ก style ํด๋๋ฅผ ๋ง๋ค์ด์ ๊ด๋ฆฌํ์ต๋๋ค.