본문 바로가기

사이드 프로젝트

[React Project] 코드 리팩토링 - 메인 페이지 tsx

오늘은 메인 페이지 tsx를 간단하게 리팩토링 해보았다. 

정리해야 할 코드들이 너무 많아서, 시간이 날 때마다 조금 씩 진행해볼까 한다. 

 

 

일단 코드 리팩토링 전 파일을 보면

 

코드 리팩토링 전

import ContainerLayout from 'src/components/common/layout/ContainerLayout'
import Content from 'src/components/common/layout/Content'
import MuiMain from '../../components/common/mui/MuiMain'
import MainHomeContent from 'src/components/specific/main/MainHomeContent'
import { UseUserInfo } from 'src/components/specific/main/hooks/MainHomeCustomHook'
import { useMemo } from 'react'

export default function MainHome() {
  const { profile, address } = UseUserInfo()

  const loc = useMemo(() => {
    return profile && address && profile.data.location
      ? {
          loc: profile.data.location,
          address: address,
        }
      : null
  }, [profile, address])

  const nickname = useMemo(() => {
    return profile ? profile.data.nickname : null
  }, [profile])

  return (
    <ContainerLayout>
      <MuiMain>
        <Content component={<MainHomeContent loc={loc} nickname={nickname} />} />
      </MuiMain>
    </ContainerLayout>
  )
}
export function UseUserInfo() {
  const { address, setCurloc, setAddress, loadingAddress } = useGetAddress()
  const [profile, setProfile] = useState<ProfileInfoType | null>(null)
  const [userNm, setUserNm] = useRecoilState(userNmStts)

  const { data, refetch, isSuccess, isPending, error, isError } = useQuery({
    queryKey: [
      'my-info',
      {
        searchParam: '',
        url: `${process.env.REACT_APP_API_DOMAIN}auth-service/member`,
      },
    ],
    queryFn: getDataTanstackWithToken,
    enabled: false,
  })

  useEffect(() => {
    refetch()
    // getRefreshToken()
  }, [])

  useEffect(() => {
    if (isObject(data) && isObject(data?.data)) {
      const castObj = castProfileInfoType(data)
      if (castObj) {
        if (castObj && castObj.data.address && castObj.data.location) {
          setAddress(`${castObj.data.address.sigungu} ${castObj.data.address.eupmyundong}`)
        } else {
          navigator.geolocation.getCurrentPosition(position => {
            const { latitude, longitude } = position.coords
            setCurloc({
              lat: latitude,
              lon: longitude,
            })
            castObj!.data!.location = {
              latitude: latitude,
              longitude: longitude,
            }
          })
        }
      }

      let name = castObj?.data.nickname || ''

      if (name?.includes('새회원')) {
        name = '새회원'
      }

      setProfile(castObj)
      setUserNm({
        params: name,
      })
    }
  }, [data])
  return {
    profile,
    isPending,
    loadingAddress,
    address,
  }
}

 

 

위에 보이는 코드가 총 코드이다. 

그리고 바꾼 부분을 천천히 설명해보고자 한다. 

 

 

1. 커스텀 훅 이름 변경 

UseUserInfo => useGetMyInfo

함수의 내용이 직관적이지 않은 것 같아 커스텀훅의 역할이 뚜렷히 보이고자 변경했다.

const { profile, address } = useGetMyInfo()

 

2. 쓸데 없는 메모제이션 지우기 

성능을 최적화 하겠답시고 memo를 넣었는데, 사실 memo는 아무데서나 쓰는게 아니라. 

렌더링 비용이 많이 드는 데에서만 선택적으로 사용하는게 좋다. 

내가 memo를 활용한 변수는 그저 변수 이름에 대한 변경 사항이기 때문에 memo를 사용하는게 불필요하다고 생각되었다.

  const getNickName = () => {
    return profile ? profile.data.nickname : null
  }

  const getLocation = () => {
    if (profile && address && profile.data.location) {
      return {
        loc: profile.data.location,
        address: address,
      }
    } else {
      return null
    }
  }

  return (
    <ContainerLayout>
      <MuiMain>
        <Content component={<MainHomeContent loc={getLocation()} nickname={getNickName()} />} />
      </MuiMain>
    </ContainerLayout>
  )

 

3. 쓰지 않는 변수 삭제하기 

userNm과 같이 쓰지 않는 변수는 삭제를 했다. 

 

4. 긴 로직은 함수로 분리하기 

원래 리팩토링 전에는 useEffect 내에 가져온 데이터를 type에 맞게 변경하고 

프로필 정보를 set하는 것 까지 모든 로직을 다 담았는데

로직이 너무 길어서 가독성이 떨어진다는 느낌을 받았다. 

그래서 mergeMyProfileAndAddress 라는 함수로 로직을 분리하여 작성하니 

훨씬 가독성이 좋아졌다. 

const mergeMyProfileAndAddress = (myInfoObj: ProfileInfoType | null) => {
    if (myInfoObj) {
      if (myInfoObj && myInfoObj.data.address && myInfoObj.data.location) {
        setAddress(`${myInfoObj.data.address.sigungu} ${myInfoObj.data.address.eupmyundong}`)
      } else {
        navigator.geolocation.getCurrentPosition(position => {
          const { latitude, longitude } = position.coords
          setCurloc({
            lat: latitude,
            lon: longitude,
          })
          myInfoObj!.data!.location = {
            latitude: latitude,
            longitude: longitude,
          }
        })
      }
    }
    setProfile(myInfoObj)
  }

  useEffect(() => {
    if (isObject(data) && isObject(data?.data)) {
      const castObj = castProfileInfoType(data)
      mergeMyProfileAndAddress(castObj)
    }
  }, [data])

 

 

 

 

 

아래는 리팩토을 진행한 후의 코드 전부이다 

 

코드 리팩토링 후

/*
    파일명 : MainHome.tsx
    설명 : 메인 화면
    작성일 : 2024-06-07
    변경일 : 2024-09-22
*/
import ContainerLayout from 'src/components/common/layout/ContainerLayout'
import Content from 'src/components/common/layout/Content'
import MuiMain from '../../components/common/mui/MuiMain'
import MainHomeContent from 'src/components/specific/main/MainHomeContent'
import { useGetMyInfo } from 'src/components/specific/main/hooks/MainHomeCustomHook'

export default function MainHome() {
  const { profile, address } = useGetMyInfo()

  const getNickName = () => {
    return profile ? profile.data.nickname : null
  }

  const getLocation = () => {
    if (profile && address && profile.data.location) {
      return {
        loc: profile.data.location,
        address: address,
      }
    } else {
      return null
    }
  }

  return (
    <ContainerLayout>
      <MuiMain>
        <Content component={<MainHomeContent loc={getLocation()} nickname={getNickName()} />} />
      </MuiMain>
    </ContainerLayout>
  )
}

 

export function useGetMyInfo() {
  const { address, setCurloc, setAddress, loadingAddress } = useGetAddress()
  const [profile, setProfile] = useState<ProfileInfoType | null>(null)

  const mergeMyProfileAndAddress = (myInfoObj: ProfileInfoType | null) => {
    if (myInfoObj) {
      if (myInfoObj && myInfoObj.data.address && myInfoObj.data.location) {
        setAddress(`${myInfoObj.data.address.sigungu} ${myInfoObj.data.address.eupmyundong}`)
      } else {
        navigator.geolocation.getCurrentPosition(position => {
          const { latitude, longitude } = position.coords
          setCurloc({
            lat: latitude,
            lon: longitude,
          })
          myInfoObj!.data!.location = {
            latitude: latitude,
            longitude: longitude,
          }
        })
      }
    }
    setProfile(myInfoObj)
  }

  const { data, refetch, isPending } = useQuery({
    queryKey: [
      'my-info',
      {
        searchParam: '',
        url: `${process.env.REACT_APP_API_DOMAIN}auth-service/member`,
      },
    ],
    queryFn: getDataTanstackWithToken,
    enabled: false,
  })

  useEffect(() => {
    refetch()
  }, [])

  useEffect(() => {
    if (isObject(data) && isObject(data?.data)) {
      const castObj = castProfileInfoType(data)
      mergeMyProfileAndAddress(castObj)
    }
  }, [data])

  return {
    profile,
    isPending,
    loadingAddress,
    address,
  }
}