본문 바로가기

코딩

주니어 React 개발자가 여전히 저지르는 12가지 useState 및 useEffect 실수

코딩환경 일러스트 이미지

 

React를 처음 배우면 마법 같은 useState와 useEffect에 빠져서 열심히 써보게 돼요. 그런데 어느 순간 예상치 못한 버그에 멘붕이 옵니다. 😵‍💫 "아니, 분명히 상태를 업데이트했는데 왜 값이 바뀌지 않지?" "왜 useEffect가 무한 루프 도는 거야?" 이런 경험, 다들 한 번쯤 있지 않나요? (고개 끄덕이면 동지 ✊) 이번 글에서는 주니어 개발자가 흔히 저지르는 12가지 실수를 정리하고, 해결 방법을 제안해볼게요.

 

🔥 실수 1. 여러 번 setState를 호출할 때 예상치 못한 결과 발생

const [count, setCount] = useState(0);

const handleClick = () => {
  setCount(count + 1);
  setCount(count + 1);
};

🚨 위 코드를 실행하면 버튼을 클릭할 때마다 1씩 증가할 것 같지만, 사실 한 번만 증가합니다. 이유는 React의 상태 업데이트는 비동기적이기 때문이에요. 해결책은 업데이터 함수를 사용해서 이전 값을 가져오는 것!

 

해결책

setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);

이렇게 하면 이전 값이 보존되면서 두 번 업데이트됩니다.

 

🔥 실수 2. 조건부 렌더링에서 Hook을 조건적으로 호출하기

if (isVisible) {
  useEffect(() => {
    console.log("이펙트 실행!");
  }, []);
}

🚨 React Hook은 무조건 렌더링 순서가 동일해야 해요. 하지만 위 코드처럼 조건문 안에서 호출하면, isVisible이 변할 때마다 useEffect의 실행 순서가 달라질 수도 있어서 오류가 납니다.

 

해결책

useEffect(() => {
  if (isVisible) {
    console.log("이펙트 실행!");
  }
}, [isVisible]);

Hook을 조건문 안에 넣지 말고, 내부에서 조건을 확인하도록 수정해 주세요.

 

🔥 실수 3. 객체 상태를 직접 변경하기

const [user, setUser] = useState({ name: "철수", age: 20 });

const updateAge = () => {
  user.age = 21; // ❌ 상태를 직접 변경
  setUser(user); // 🚨 React는 이걸 감지하지 못함
};

🚨 React에서 상태를 직접 변경하면 변경 사항을 감지하지 못해서 리렌더링이 발생하지 않아요.

 

해결책 (객체를 복사한 후 업데이트)

setUser(prevUser => ({ ...prevUser, age: 21 }));

전개 연산자(...)를 사용해서 새로운 객체를 만들어야 합니다.

 

🔥 실수 4. 많은 input이 있는 폼에서 각각의 상태를 관리하기

const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

🚨 이렇게 하면 관리해야 할 상태가 많아지고 코드가 지저분해집니다.

 

해결책 (하나의 객체로 상태 관리)

const [form, setForm] = useState({ name: "", email: "", password: "" });

const handleChange = (e) => {
  setForm(prevForm => ({ ...prevForm, [e.target.name]: e.target.value }));
};

이렇게 하면 handleChange 하나로 모든 입력 값을 관리할 수 있어요.

 

🔥 실수 5. 불필요한 useEffect 사용

const [price, setPrice] = useState(0);
const [total, setTotal] = useState(0);

useEffect(() => {
  setTotal(price * 1.1);
}, [price]);

🚨 이렇게 하면 price가 바뀔 때마다 불필요한 리렌더링이 발생해요.

 

해결책 (그냥 변수로 계산하기)

const total = price * 1.1;

state에 저장할 필요 없이, 그냥 변수로 계산해서 사용하면 됩니다.

 

🚀 12가지 실수를 한눈에 보기 쉽게 정리!

# 실수 유형 해결 방법
1 setState 여러 번 호출 시 예상치 못한 결과 업데이터 함수 사용 (prevState => prevState + 1)
2 조건문 안에서 Hook 호출 조건문 밖에서 호출, 내부에서 조건 확인
3 객체 상태 직접 변경 새로운 객체를 생성 후 업데이트
4 폼에서 각각의 상태 관리 객체 하나로 상태 관리
5 불필요한 useEffect 사용 변수로 계산
6 useEffect에서 데이터 가져오기 React Query 또는 SWR 사용
7 클로저 문제로 인한 setInterval 버그 useRef 또는 클린업 함수 사용
8 원시 값 vs 비원시 값 차이 이해 부족 객체를 dependency로 넣을 때 useMemo 활용
9 서버 컴포넌트에서 useState 사용 use client 지시어 추가
10 TypeScript에서 타입 명시 안 함 `Post
11 커스텀 Hook을 사용하지 않음 공통 로직을 커스텀 Hook으로 분리
12 비동기 요청 취소 안 함 AbortController로 이전 요청 취소

 

React 초보일 때는 이런 실수를 하면서 성장하는 거니까 너무 좌절하지 마세요! (저도 다 겪었습니다 😂) 하지만 이런 문제를 미리 알고 있으면 더 빠르게 성장할 수 있겠죠?

 

혹시 추가로 헷갈리는 부분이나 궁금한 점 있으면 댓글로 남겨주세요. 우리 같이 성장해봐요! 🚀