목차

    ⏳ Time Log/1. One Day (Daily · TIL)

    Day 30 (11/28) - [React] Todo 앱 만들기 핵심 요약 및 트러블슈팅

    this.Serena 2026. 2. 20. 12:01

    학습 목표: React Todo 앱 실습을 통한 JSX 문법, 조건부 렌더링, 이벤트 핸들링 및 실무 패턴 습득

    1. React JSX 괄호 구분 가이드

    JSX에서 중괄호 {}와 소괄호 ()는 명확한 용도 차이가 있습니다.

    • 중괄호 {} : JavaScript 표현식 삽입
      • 내부 텍스트: 변수나 로직 삽입 시 사용 ({name})
      • 배열 렌더링: {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
      • 속성 값: value={content}
      • 인라인 스타일 (객체 전달): style={{ color: 'red' }}
      • 특수문자 출력: > 기호 등은 {'>'} 또는 {'}'} 처럼 문자열로 감싸서 처리
    • 소괄호 () : 긴 JSX 반환 시 가독성 확보
      • 여러 줄의 JSX를 반환할 때 하나의 묶음으로 처리하여 가독성을 높임
      • 화살표 함수에서 직접 반환 시 유용: () => (<div>...</div>)
    // ❌ 가독성이 떨어지는 방식
    return <div><h1>제목</h1><p>내용</p></div>;
    
    // ✅ 권장하는 방식 (소괄호 사용)
    return (
      <div>
        <h1>제목</h1>
        <p>내용</p>
      </div>
    );

    2. 주요 로직 및 이벤트 핸들링

    2.1 검색어 필터링 (대소문자 무시)

    검색 기능 구현 시 toLowerCase()를 활용하면 영문 대소문자 구분 없이 필터링이 가능합니다.

    const getSearchResult = () => {
        return search === ""
        ? todo
        : todo.filter((it) =>
            it.content.toLowerCase().includes(search.toLowerCase())
        );
    };

    2.2 엔터키(Enter) 입력 핸들러

    버튼 클릭뿐만 아니라 엔터키를 눌렀을 때도 폼이 제출되도록 처리합니다.

    const handleKeyDown = (e) => {
      // e.keyCode === 13 과 동일한 역할
      if (e.key === 'Enter') {
        onSubmit();
      }
    };

    2.3 삼항 연산자를 활용한 상태 업데이트 (리팩토링)

    조건문(if-else)을 삼항 연산자로 변경하여 코드를 간결하게 작성할 수 있습니다.

    // ❌ 수정 전 (if-else 사용)
    const onUpdate = (targetId) => {
      setTodo(
        todo.map((it) => {
          if (it.id === targetId) {
            return { ...it, isDone: !it.isDone };
          } else {
            return it;
          }
        })
      );
    };
    
    // ✅ 수정 후 (삼항 연산자 사용)
    const onUpdate = (targetId) => {
      setTodo(
        todo.map((it) =>
          it.id === targetId ? { ...it, isDone: !it.isDone } : it
        )
      );
    };
    

    3. ⚠️ 트러블슈팅 및 자주 발생하는 에러

    🚨 에러 1: child in a list should have a unique "key" prop

    React에서 map() 함수로 배열을 렌더링할 때 각 아이템에 고유한 key 속성을 부여하지 않아 발생하는 경고입니다.

    React는 가상 DOM(Virtual DOM)을 비교할 때 이 key를 통해 어떤 항목이 추가/삭제/이동되었는지 추적합니다. key가 없으면 성능이 저하되고 예기치 않은 렌더링 오류가 발생할 수 있습니다.

    // ❌ 잘못된 예 (key 누락)
    {todos.map(todo => <li>{todo.text}</li>)}
    
    // ✅ 올바른 예 (고유한 id 사용 권장)
    {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
    
    // 🔺 주의: 데이터에 고유 id가 전혀 없을 때만 최후의 수단으로 index 사용
    {todos.map((todo, index) => <li key={index}>{todo.text}</li>)}
    

    🚨 에러 2: setContent("") 실행 후 입력창이 초기화되지 않는 현상

    폼 제출 후 setContent("")를 호출했음에도 input 창이 비워지지 않는다면, input 태그에 value={content} 속성이 누락되었기 때문입니다. 상태(State)와 UI를 동기화하려면 반드시 value 속성을 연결해야 합니다.

    <input
      ref={inputRef}
      value={content} // 💥 이 부분이 누락되면 초기화가 반영되지 않음
      onChange={onChangeContent}
      onKeyDown={handleKeyDown}
      placeholder="새로운 Todo..."
    />
    

    🚨 에러 3: 체크박스 클릭 시 상태 업데이트 안 됨 (오타 이슈)

    객체 스프레드 연산자를 사용할 때 속성명에 오타가 발생하면 기존 상태가 덮어씌워지지 않고 새로운 속성이 추가되어 버그가 발생합니다.

    • ❌ 오타 발생: { ...it, idDone: !it.isDone }
    • ✅ 정상 코드: { ...it, isDone: !it.isDone }

    4. 🗓️ 개인 메모 및 향후 일정

    • 현재 진행: 리액트 한입 Todo 앱 만들기 완료 (2025-11-28)
    • 학습 예정:
      • 7장: useReducer와 상태 관리 학습
      • 12/1: 한입 리액트 감정일기장 만들기 (페이지 라우팅 핵심)
    • 프로젝트 플랜:
      • 과일농장 게시판 변형: Django를 이용한 CRUD 로직 구현 (커리큘럼상 형식적 진행)
      • Spring Boot + React 연동 프로젝트: 2월부터 6주간 진행 예정인 핵심 프로젝트
    • 참고 자료: React 공식 튜토리얼 (Tic-Tac-Toe)
    • 개념 정리:
      • export: 파일 외부에서 접근할 수 있도록 함수를 내보냄
      • export default: 해당 파일의 주요 함수(메인 컴포넌트)임을 명시