목차

    불편함을 코드로 지웁니다.

    개발 공부 노트, 개발 도구와 문제를 해결한 과정, AI 프롬프트와 Obsidian 활용법을 정리합니다.

      ⏳ Time Log/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: 해당 파일의 주요 함수(메인 컴포넌트)임을 명시