Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

일상

16주차 - React 본문

교육

16주차 - React

콜리/khgeung 2025. 9. 21. 16:55

1. React 기초

1-1. React

 

1-2. 화살표 함수

const handleClick = () => {
        alert("React 처럼 이벤트 처리");
        console.log("화살표 함수로 이벤트 처리");
      };

1-3. 구조 분해 할당

      // 2. 구조 분해 할당 (destructuring assignment) - React props 와 useState 필수
      const user = { name: "손흥민", age: 33, city: "LA" };
      // user 객체의 변수 정보가 아주 많다고 가정
      // user 객체의 일부 name 과 city 만 필요하여 추출하여 새로운 객체로 만들고자 함
      // 객체의 주솟값을 비교하므로 객체의 주솟값이 달라지지 않으면 비교를 안함
      const { name, city } = user; // 필요한 것만 추출
      console.log("구조 분해 할당으로 추출: ", name, city);
      //React useState 패턴 (아주 많이 쓰임)
      const [count, setCount] = [
        7,
        (count) => {
          console.log(count);
        },
      ];
      console.log("useState 패턴:", count);
      console.log(setCount);
      setCount(9); // 리액트에서는 이렇게 set 메소드를 이용해야 화면에 렌더링된다.
      // React props : 컴포넌트 외부 (상위)에서 컴포넌트로 정보 전달할 때 사용하는 기술
      const profile = ({ name, age }) => {
        return `${name} ${age}세`;
      };
      console.log("구조분해할당 테스트", profile({ name: "조규성", age: 28 }));
      //구조분해 할당 시 기본값 설정
      const profile2 = ({ name = "익명", age = 0 }) => {
        return `${name} ${age}세`;
      };
      console.log("구조분해할당 테스트2", profile2({}));

1-4. 스프레드 연산자

  • React state 배열 업데이트 시 사용한다.
 //3. 스프레드 연산자 - React state -> 불변성 필수
      // React state 배열 업데이트 시 사용한다
      const todos = ["여행하기", "운동하기"];
      const newTodos = [...todos, "쇼핑하기"]; // 새 배열 생성
      console.log("배열 불변성:" + newTodos);
      // React state 객체 업데이트
      const userState = { name: "손흥민", age: 30 };
      const updatedUser = { ...userState, city: "서울" };
      console.log("객체 불변성:", updatedUser);
      //아래와 같이 속성을 중복 명시하면 update가 된다.
      const updatedUser2 = { ...userState, city: "서울", age: 33 };
      console.log("객체 불변성:", updatedUser2);
      /*
        React setState 실제 사용 패턴
        const updateState = (currentState, updates) => {
          return {currentState, ...update}; // 기존 + 새속성
        }
      */

1-5. map() 메소드

 //4. map() 메소드 : React 리스트 렌더링 시 사용
      const users = [
        { id: 1, name: "손흥민", age: 33 },
        { id: 2, name: "조규성", age: 28 },
        { id: 3, name: "카스", age: 26 },
      ];
      // React 컴포넌트 리스트 렌더링시 key 포함 필수
      const userComponents = users.map((user) => {
        return {
          key: user.id,
          jsx: `<div key="${user.id}">${user.name} ${user.age}세`,
        };
      });
      console.log("React 리스트 리렌더링 map");
      userComponents.forEach((item) => console.log(item.key, item.jsx));

1-6. 조건부 렌더링

  • fiter 조건식이 true이면 배열 요소로 추가한다.
    • const adults = users.filter((user)=>user.age >=30);
  • 조건부 렌더링 핵심
    1. 삼항 연산자 : { 조건 ? <A/> : <B/> }
      • 조건이 true 이면 A, false 이면 B를 렌더링
    2. && 연산자 : { 조건 && <컴포넌트 /> }
      • 조건이 true 일때만 컴포넌트 렌더링
    3. if/else 문으로도 가능하지만 jsx 안에서는 삼항연산자가 일반적
    1.  

2. React Component

2-1. 리액트 컴포넌트

  • 컴포넌트는 UI를 구성하는 독립적이고 재사용 가능한 부품 
  • React 컴포넌트는 jsx를 반환
  • children : 컴포넌트 태그 사이에 포함된 모든 컨텐트를 나타내는 특별한 props
function TabButton({ children, onSelect, isSelected }) {
  return (
    <li>
      <button className={isSelected ? "active" : undefined} onClick={onSelect}>
        {children}
      </button>
    </li>
  );
}
export default TabButton;

2-2. Props

  • 부모 컴포넌트가 자식 컴포넌트에게 데이터를 전달하는 기술 
  • 자식 컴포넌트에서는 읽기 전용으로만 사용 가능
    • ex) 블로그 포스트 목록, 쇼핑몰 상품카드, 게시판 글 목록 등에서 사용될 수 있음
  • 선택적 Props : 조건부 렌더링
    • { props.author &&  <코드> } : props.author 가 존재할 때만 렌더링
//부모 컴포넌트
  <LikeButton postTitle="손흥민" />
  
//자식 컴포넌트
function LikeButton({ postTitle }) { ... }

 

2-3. React Hook

  • React State Hook 동작 과정
    1. hook 함수인 setCount(count + 1) 호출
    2. React 가 상태 변경을 감지
    3. 컴포넌트 함수를 다시 실행 (리렌더링)
    4. 새로운 count 즉 상태 값으로 화면 업데이트
import { useState } from "react";
import "./TodoList.css";
/*
이 예제에서 구현해볼 것:
- todos 배열을 map으로 순회하며 li 요소 생성
- 각 li에 key={todo.id} 필수 설정
- 상태 변경시 자동 리렌더링
- 빈 배열일 때 조건부 렌더링
*/
function TodoList() {
  //로직 : javascript
  // To do list 상태 관리 - react hook useState
  // to do list 상태 정보를 저장하는 todos 변수, to do list 상태를 변화시키기 위한 함수 setTodos
  // 아래 useState(초기값) 은 실제로는 API Server 연동을 통해 확보하지만 지금은 직접 입력한다 (JSON Array)
  const [todos, setTodos] = useState([
    { id: 1, text: "점심 먹기", completed: false },
    { id: 2, text: "카드 놀이", completed: true },
  ]);
  //새로운 to do 입력 상태
  const [newTodo, setNewTodo] = useState("");

  //할 일 to do 추가 함수
  const addTodo = () => {
    if (newTodo.trim()) {
      const todo = {
        id: Date.now(), //시간정보를 이용해 고유 아이디 생성
        text: newTodo,
        completed: false,
      };
      setTodos([...todos, todo]); //기존 배열에 새 항목 추가한 새 배열 생성해 할당
      setNewTodo(""); //입력창 초기화
    }
  };
  //To do 항목 삭제 함수
  const deleteTodo = (id) => {
    //console.log("삭제할 to do id " + id);
    //to do list 중 해당 id를 가진 to do 요소를 삭제하고 to do list 를 리렌더링 하기 위해서는 react hook state를 이용해야 한다 => 변경 위해서는 useState hook 함수가 반환한 두번째 요소인 set계열 함수를 이용해 업데이트해야 한다.
    // filter 함수는 todo.id != id 이 true 이면 새 배열 요소로 추가
    //다시 말하면 삭제할 to do id 이면 != 에 의해 false 가 나올 것이고 이는 새 배열 요소에서 제외됨 -> 즉 삭제 효과
    setTodos(todos.filter((todo) => todo.id != id));
  };
  //화면 렌더링 : jsx
  return (
    <div className="todo-list">
      <h2>할 일 목록</h2>

      {/* 새 할 일 추가 */}
      <div className="add-todo">
        <input
          type="text"
          placeholder="새 할 일을 입력하세요"
          value={newTodo} //입력 폼 요소 값 value를 리액트 state 상태 값으로 관리
          onChange={(event) => setNewTodo(event.target.value)} //입력 요소 value가 change 변경될 때 react state hook 의 함수로 상태를 변경 -> 앱에 리렌더링
          //아래는 key 를 눌렀을 때 발생하는 이벤트

          //keyDown 이벤트 발생 시 아래 화살표 함수 arrow function 를 등록 binding
          //addTodo가 아니라 addTodo() 로 명시한 이유는 엔터키 이벤트가 발생 시에 바로 호출 즉 실행되어 todo 를 추가하기 위해 즉 구현부 내에서는 실행을 해야 하므로 반드시 () 를 명시해야 됨
          //즉 addTodo() = 실행 내용 (구현부 내)
          onKeyDown={(e) => e.key === "Enter" && addTodo()} //key를 눌렀을 때 발생하는 이벤트
        />
        {/* 아래는 버튼이 클릭되어지면 (클릭 이벤트) 실행될 함수를 binding 등록 */}
        {/* onClick 의 addTodo의 구현부 (if() ~)를 실행하라고 등록함 */}
        {/* 함수를 호출하는 게 아니라 호출될 함수 객체를 등록하는 것임 */}
        <button onClick={addTodo}>추가</button>
        {/* onChange={(event) => setNewTodo(event.target.value)} */}
      </div>

      {/* 리스트 렌더링 - map() 메서드 사용 */}

      <ul className="todo-items">
        {todos.map((todo) => (
          //리액트 리스트에서는 key를 반드시 설정해야 함 -> 오류 방지 및 성능 향상
          <li
            key={todo.id}
            className={`todo-item ${todo.completed ? "completed" : undefined}`}
          >
            <span className="todo-text">{todo.text}</span>
            {/* 삭제 버튼이 클릭되면(클릭이벤트 발생 시) 실행될 화살표함수를 등록
                이후 삭제 버튼을 클릭하면 함수 구현부에서  deleteTodo 함수를 실행하여 to do item을 삭제
             */}
            <button className="delete-btn" onClick={() => deleteTodo(todo.id)}>
              삭제
            </button>
            {/*   const deleteTodo = (id) => {
                  console.log("삭제할 to do id " + id);
                  }; 
            */}
          </li>
        ))}
      </ul>

      {/* to do list 가 비어 있을 때 아래를 보이도록 조건 처리한다 */}
      {todos.length === 0 && <p className="empty-message">할 일이 없습니다!</p>}

      <div className="debug">
        <p>총 할 일: {todos.length}개</p>
      </div>
    </div>
  );
}
export default TodoList;

2-4. 리스트 렌더링

  • 리스트 렌더링 핵심
    1. map() 메소드 : 배열.map((item) => <jsx key ={item.id}>)
      • 배열의 각 요소를 jsx로 변환
    2. key prop : React가 리스트를 효율적으로 관리
      • 고유한 값 사용 (id, index 등)
      • key가 없으면 경고 발생
    3. 동적 리스트 : 상태 변경으로 추가/삭제
      • spread 연산자로 새 배열 생성
      • filter 로 항목 제거
function TodoList() {
  //로직 : javascript
  // To do list 상태 관리 - react hook useState
  // to do list 상태 정보를 저장하는 todos 변수, to do list 상태를 변화시키기 위한 함수 setTodos
  // 아래 useState(초기값) 은 실제로는 API Server 연동을 통해 확보하지만 지금은 직접 입력한다 (JSON Array)
  const [todos, setTodos] = useState([
    { id: 1, text: "점심 먹기", completed: false },
    { id: 2, text: "카드 놀이", completed: true },
  ]);
  //새로운 to do 입력 상태
  const [newTodo, setNewTodo] = useState("");

  //할 일 to do 추가 함수
  const addTodo = () => {
    if (newTodo.trim()) {
      const todo = {
        id: Date.now(), //시간정보를 이용해 고유 아이디 생성
        text: newTodo,
        completed: false,
      };
      setTodos([...todos, todo]); //기존 배열에 새 항목 추가한 새 배열 생성해 할당
      setNewTodo(""); //입력창 초기화
    }
    
    return ( ... );
    
  };
  • 예제
import { useState } from "react";
import CoreConcept from "./components/CoreConcept.jsx";
import Header from "./components/Header/Header.jsx";
import TabButton from "./components/TabButton.jsx";
import { CORE_CONCEPTS } from "./data/data.js";
import { EXAMPLES } from "./data/data.js";

function App() {
  // state 상태 관리 : useState Hook 을 이용
  // selectedTopic : 사용자가 어떤 탭을 선택했는지 탭제목을 저장,
  // setSelectedTopic : 사용자가 탭을 선택했을 때 React 에 탭 정보 변경을 알려서 리렌더링 되도록 하는 함수
  const [selectedTopic, setSelectedTopic] = useState();
  //탭 버튼 클릭시 호출되는 함수 (탭버튼 이벤트 핸들러)
  //탭버튼 정보 : components, jsx, props, state 중 하나
  //(data.js 내의 json 정보)
  function handleSelect(selectedButton) {
    //react state hook 함수를 호출해 화면 리렌더링을 한다
    setSelectedTopic(selectedButton);
  }
  console.log("Component rendering");
  let tabContent = <p>주제를 선택하세요</p>;
  //선택된 토픽이 있으면 해당 내용을 표시
  if (selectedTopic) {
    tabContent = (
      <div id="tab-content">
        <h3>{EXAMPLES[selectedTopic].title}</h3>
        <p>{EXAMPLES[selectedTopic].description}</p>
        <pre>{EXAMPLES[selectedTopic].code}</pre>
      </div>
    );
  }
  return (
    <div>
      {/* Header 컴포넌트 렌더링 */}
      <Header />
      <main>
        {/* ===== 섹션 1: Core Concepts ===== */}
        <section id="core-concepts">
          <h2>Core Concepts</h2>
          <ul>
            {/* map()을 사용한 동적 리스트 렌더링 */}
            {/* key prop은 React가 각 항목을 추적하는데 필요
            {CORE_CONCEPTS.map((conceptItem) => {
              return (
                <CoreConcept
                  key={conceptItem.title}
                  image={conceptItem.image}
                  title={conceptItem.title}
                  description={conceptItem.description}
                />
              );
            })} 
            */}
            {/* 스프레드 연산자로 효율적으로 props 전달, 화살표 함수를 간략하게 표현 {}가 아니라 () 형태로
                {...conceptItem}  표현은 image={conceptItem.image} title={conceptItem.title}
                description={conceptItem.description} 와 동일한 표현임

                화살표 함수 { } return 시에 return keyword 를 명시
                화살표 함수 ( ) 리턴 생략  => 화살표 함수 축약 형태이고 실행문이 한 문장일 때 사용할 수 있다.
            */}
            {CORE_CONCEPTS.map((conceptItem) => (
              <CoreConcept key={conceptItem.title} {...conceptItem} />
            ))}
          </ul>
        </section>
        {/* ===== 섹션 2: Examples ===== */}
        <section id="examples">
          <h2>Examples</h2>
          <menu>
            {/* 각 TabButton에 이벤트 핸들러와 선택 상태 전달 */}
            <TabButton
              isSelected={selectedTopic === "components"}
              onSelect={() => handleSelect("components")}
            >
              Components
            </TabButton>
            <TabButton
              isSelected={selectedTopic === "jsx"}
              onSelect={() => handleSelect("jsx")}
            >
              jsx
            </TabButton>
            <TabButton
              isSelected={selectedTopic === "props"}
              onSelect={() => handleSelect("props")}
            >
              props
            </TabButton>
            <TabButton
              isSelected={selectedTopic === "state"}
              onSelect={() => handleSelect("state")}
            >
              state
            </TabButton>
          </menu>
          {/* 조건부로 렌더링되는 컨텐츠 */}
          {tabContent}
        </section>
      </main>
    </div>
  );
}
export default App;
/*
    CoreConcept 컴포넌트
    - Props : 부모 컴포넌트에서 자식 컴포넌트로 정보를 전달, 자식 컴포넌트에서는 구조분해할당 destructuring 받아서 처리
    - 컴포넌트 재사용성 연습을 위해 CoreConcept 컴포넌트를 정의
*/
function CoreConcept({ image, title, description }) {
  return (
    <li>
      <img src={image} alt={title} />
      <h3>{title}</h3>
      <p>{description}</p>
    </li>
  );
}
export default CoreConcept;

3. React LifeCycle

3-1. React LifeCycle

  • 마운트 시에만 실행 (의존성 빈 배열) : componentDidmount
    • api 호출, 초기 데이터 설정 등에 사용
    • useEffect cleanup : 언마운트 시에 실행할 작업을 화살표 함수로 정의해 리턴
useEffect(() => {
    //API 호출, 초기 데이터 설정 등에 사용
    console.log("1번 useEffect:컴포넌트가 마운트되었습니다");
    //useEffect cleanup : 언마운트 시에 실행할 작업을 화살표 함수로 정의해 리턴한다
    return () => {
      console.log(
        "1번 useEffect : 컴포넌트가 언마운트됩니다! 정리 작업 cleanup을 수행합니다"
      );
    };
  }, []); //빈 의존성 배열 -> 마운트 시에 1회만 실행
  • 모든 렌더링마다 실행 (의존성 배열 없음)
    • 거의 사용하지 않는다. (성능 이슈)
 useEffect(() => {
    //거의 사용하지 않음 - 성능 이슈 주의
    console.log("2번 useEffect:렌더링이 일어났습니다");
  }); //의존성 배열을 넣지 않은 상태
  • 특정 상태 변경 시 실행 : componentDidUpdate
    • state or props 가 변경 시 렌더링될 때 실행
  useEffect(() => {
    //state or props 가 변경 시 렌더링될 때 실행
    console.log(`3번 useEffect:count가 변경됨: ${count}`);
  }, [count]); //의존성 배열을 넣은 상태
  //즉 count가 바뀌면 실행됨

4. axios

4-1. fetch 와 axios의 특징

  fetch axios
데이터 파싱 response.json() response.data로 자동 파싱
코딩 스타일 2개 await가 필요(head, body) 각각 await 한번만 하면 됨
  • 예제
 const getUserData = async () => {
        try {
          const response = await axios.get(
            "https://jsonplaceholder.typicode.com/users/1"
          );
          const userData = response.data; //axios 는 응답 데이터를 response.data에 담아줌
          console.log(userData.name);
          alert("axios : " + userData.name);
          return userData;
        } catch (error) {
          console.error("에러 발생:", error.message);
        }
      };

'교육' 카테고리의 다른 글

15주차 - JPA  (0) 2025.09.14
14주차 - Ajax  (0) 2025.08.10
13주차 - Thymeleaf  (0) 2025.07.28
12주차 - Spring  (0) 2025.07.20
11주차 - Design Pattern  (0) 2025.07.13