입력값 매우 많은 페이지 리팩토링& 성능개선

김민지·2026년 1월 4일

인턴 정리

목록 보기
4/7

초기 상태

컴폰언트 안에서 관리하던 state값을 밖으로 뺌... 그리고 state 관리 방식도 바꿈.
렌더링 수를 줄임.
그리고 mui accordion 추가하니까 너무 느려져서 뺌

체감은 훨씬 빨라졌는데 성능 점수는 낮아졌다. 알아보니까 mui accordion은 이미 최적화가 잘 되어 있어서 성능 점수는 더 높게 나올 수도 있대.

이제 다른 방식으로 성능개선해야 함.
우선 이 페이지는 여러 api를 동시에 호출해야 하는 것이 큰 단점(?)임. 이걸 잘 해야 하는데 지금은 그냥 아래처럼 되어 있다

...생략
  const CHECKBOX_FIELDS = [
    'jobUuids',
    'educationLevels',
    'employmentTypes',
    'region',
    'deadlineType',
  ]
...생략
  useEffect(() => {
    const fetchData = async () => {
      CHECKBOX_FIELDS.forEach((field) => {
        const mappedType = fieldToTypeMapping[field]
        if (mappedType) {
          getRegisterOptions(mappedType).then((response) => {
            if (response) {
              const options = response[mappedType]
              if (options) {
                setRegisterOptions((prevOptions) => ({
                  ...prevOptions,
                  [field]: options,
                }))
              }
            }
          })
        }
      })
      try {
        const response = await getRecruitmentCompany()
        if (response && response.length > 0) {
          setCompanyList(response)
        }
      } catch (error) {
        console.error(error)
      }
    }
    fetchData()
  }, [])


그래도 딱 한 번 호출되긴 함

문제점
근데 문제점이 CHECKBOX_FIELDS 항목에 대해 Api를 순차적으로 호출하면 시간이 오래 걸릴 수도 있다고 한다. 그리고 getRecruitmentCompany 이거도 순차적으로 호출하고 있다.

해결 방법
api를 병렬로 호출하면 효율성이 높아질 수 있다 한다. 이건 Promise.all을 쓰면 된다.

  const fetchOptionsData = async () => {
    try {
      const fetchPromises = CHECKBOX_FIELDS.map((field) => {
        const mappedType = fieldToTypeMapping[field]
        if (mappedType) {
          return getRegisterOptions(mappedType).then((response) => {
            if (response) {
              const options = response[mappedType]
              if (options) {
                return {
                  field,
                  options,
                }
              }
            }
            return null
          })
        }
        return null
      })

      const companyPromise = getRecruitmentCompany().then((response) => {
        if (response && response.length > 0) {
          setCompanyList(response)
        }
      })

      const [results] = await Promise.all([
        Promise.all(fetchPromises),
        companyPromise,
      ])

      const newRegisterOptions = {
        jobUuids: [] as string[],
        educationLevels: [] as string[],
        region: [] as string[],
        deadlineType: [] as string[],
      }

      results.forEach((result) => {
        if (result && result.options) {
          newRegisterOptions[result.field] = result.options
        }
      })

      setRegisterOptions(newRegisterOptions)
    } catch (error) {
      console.error(error)
    }
  }

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

근데 역시 성능에는 변화가 별로 없다. 중복 호출 문제가 없었기 때문인 것 같다.

CLS(Cumulative Lazy Loading) 줄이기

const PreviewImage = styled.img`
  width: 00px;
  max-width: 400px;
  height: auto;
  margin: 20px 0;
  will-change: opacity, transform;
  transition: opacity 0.3s ease-in-out;
  loading: lazy;
`

그래서

const PreviewImage = styled.img`
  width: 400px;
  height: 400px;
  object-fit: contain;
  margin: 20px 0;
  will-change: opacity, transform;
  transition: opacity 0.3s ease-in-out;
  loading: lazy;
`

이렇게 해줬는데도 성능 개선 효관 없었다. img가 하나여서 그런듯

어쩔 수 없다. ssr로 필요한 데이터를 미리 받아와야겠다.
ssr 처음 써보는데 이거 cookie로 accesstoken 가져오는게 좀 별로네.
페이지 라우팅, next.js13 쓰는 중인데...
페이지 index.tsx에서 getServerSideProps 함수 만들었고 이 안에서 getRegisterOptions이런 걸 쓰고 있다. getRegisterOptions이건 fetchData라는 api 호출 함수를 사용 중인데, 원래 fetchData에서 토큰을
그냥

const getCookie = (name: string): string | null => {
  const matches = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
  return matches ? matches[2] : null
}

이렇게 쓰고 있었다. 근데 ssr에서는 document를 사용 못한다고 해서... fetchData에서 토크을 바로 가져올 수 있으면 좋은데 안되더라.
페이지의 index.tsx 에서 context: GetServerSidePropsContext로

    const cookies: Record<string, string> = Object.fromEntries(
      Object.entries(context.req.cookies).filter(
        ([key, value]) => value !== undefined,
      ),
    ) as Record<string, string>
    const accessToken = cookies.accessToken || null

이렇게 사용해서 props로 넘겨줄 수밖에 없었다.

그래도 성능이 거의 두 배 향상되긴 했다.

profile
이건 대체 어떻게 만든 거지?

0개의 댓글