← 전체 글로 돌아가기

Next.js

React 컴포넌트 props가 운영에서 다르게 동작할 때

개발 환경과 프로덕션 환경에서 컴포넌트가 다르게 보이거나 동작하는 경우와 디버깅 방법.

컴포넌트가 로컬에서는 완벽한데, 프로덕션 서버에 배포하면 props가 제대로 전달되지 않거나 다르게 렌더링된다. 이는 대개 환경 차이, 빌드 최적화, 혹은 런타임 에러 때문이다.

먼저 프로덕션 빌드를 로컬에서 테스트한다

운영과 동일하게 빌드하고 실행한다.

npm run build
npm run start

이제 localhost에서 프로덕션 빌드로 테스트할 수 있다. 로컬 개발 서버(npm run dev)와 다르게 동작하나?

콘솔 에러를 본다

Browser DevTools에서 Console 탭을 연다. 에러나 경고가 있나?

Warning: You provided a 'checked' prop to a form field without an 'onChange' handler.

이런 경고는 보통 props 사용법이 잘못됐다는 뜻이다.

props의 타입을 확인한다

TypeScript를 쓴다면, props 타입이 명확한가?

interface ButtonProps {
  onClick: () => void
  disabled?: boolean
  children: React.ReactNode
}

function Button({ onClick, disabled = false, children }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled}>
      {children}
    </button>
  )
}

프로덕션 빌드에서는 TypeScript가 체크되지 않으므로, 런타임에 엉뚱한 타입이 올 수 있다.

props의 기본값을 확인한다

function MyComponent({ title = 'Default Title' }) {
  return <h1>{title}</h1>
}

기본값이 없으면 undefined가 올 수 있고, 예상과 다르게 렌더링된다.

부모 컴포넌트가 제대로 전달하고 있나?

부모에서 props를 제대로 주고 있나?

// 부모
const user = { name: 'John', age: 30 }
return <UserCard {...user} />

// 자식
function UserCard({ name, age }) {
  return <div>{name}, {age}세</div>
}

DevTools에서 부모의 state를 보면서, 자식에게 뭐가 전달되는지 추적한다.

함수 props를 전달할 때 화살표 함수인가?

// 잘못된 예
<Button onClick={handleClick()} />

// 올바른 예
<Button onClick={handleClick} />
<Button onClick={() => handleClick()} />

첫 번째는 렌더링할 때 즉시 함수를 호출해버린다.

불변성을 지키고 있나?

React는 props가 바뀌었을 때 리렌더링한다. 하지만 객체나 배열의 레퍼런스가 바뀌지 않으면 감지하지 못할 수 있다.

// 잘못된 예
const items = props.items
items.push(newItem)  // 같은 배열을 변경
setItems(items)

// 올바른 예
const newItems = [...props.items, newItem]  // 새 배열 생성
setItems(newItems)

useMemo, useCallback이 필요한가?

렌더링 성능이 문제라면, 의존성 최적화를 고려한다.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
const memoizedCallback = useCallback(() => handleClick(), [dependency])

잘못된 의존성 배열은 의도하지 않은 리렌더링을 일으킨다.

Context나 Redux 상태가 제대로 초기화되나?

전역 상태를 쓴다면, 새로고침할 때 초기값으로 리셋되는가?

const UserContext = createContext()

function UserProvider({ children }) {
  const [user, setUser] = useState(null)

  useEffect(() => {
    // 앱 시작할 때 사용자 정보를 로드한다
    loadUser()
  }, [])

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  )
}

Provider가 제대로 렌더 트리를 감싸고 있나?

환경변수가 빌드에 포함됐나?

Next.js에서 환경변수를 쓸 때, 빌드 시에 포함되지 않으면 런타임에 undefined가 된다.

# .env.local
REACT_APP_API_URL=https://api.example.com

클라이언트 사이드 코드에서 쓸 환경변수는 REACT_APP_ 접두사를 붙여야 한다.

라이브러리 버전이 다르지는 않나?

로컬과 프로덕션에서 설치된 패키지 버전이 다를 수 있다.

npm ls react  # 로컬
ssh user@server "npm ls react"  # 프로덕션

buggy 버전의 라이브러리가 프로덕션에만 설치돼있을 수도 있다.

결론

Props 관련 문제는 대개 타입 불일치, 기본값 누락, 혹은 의존성 관리 실수다. 로컬에서 프로덕션 빌드로 테스트하고, 콘솔 에러를 읽고, DevTools로 props를 직접 확인하면 원인을 빠르게 찾을 수 있다.