🌱SeSAC x CodingOn 웹 취업 부트캠프

[새싹/코딩온] 풀스택 웹 개발자 취업 부트캠프 11주차 (2): React Props & State, Event Handling, map(), filter(), Life Cycle

haeriyouu 2025. 1. 20. 12:45

Props & State

💻 Props (Properties)

- 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 방법 (상위➡️하위 만 가능!)

- 여러 번 쓰이는 컴포넌트에서 내부 데이터만 다르게 사용하고 싶을 때 사용

- 컴포넌트끼리 값을 전달하는 수단

// Class형 props

import { Component } from "react";

// props는 반드시 객체이다.

class ClassProps extends Component {
  render() {
    const divStyle = {
      color: this.props.color,
    };
    return (
      <div style={divStyle}>
        <h4>hi, {this.props.name}</h4>
        <ul>
          <li>별명: {this.props.nickname}</li>
          <li>좋아하는 색: {this.props.color}</li>
        </ul>
      </div>
    );
  }
}

class ClassProps2 extends Component {
  render() {
    const { name, color, nickname, fontColor } = this.props;
    const divStyle = {
      color: fontColor,
      backgroundColor: color,
    };
    return (
      <div style={divStyle}>
        <h4>hi, {name}</h4>
        <ul>
          <li>별명: {nickname}</li>
          <li>좋아하는 색: {color}</li>
        </ul>
      </div>
    );
  }
}

ClassProps2.defaultProps = { fontColor: "beige" };

export { ClassProps, ClassProps2 }; // 여러개를 내보낼때는 객체 형태로 보내야 한다.

💻 State

- 컴포넌트 내부에서 관리되는 변경 가능한 데이터

- 가변성!

// Class형 state

import { Component } from "react";

export default class ClassState extends Component {
  // Class 형에서는 state가 객체형으로 관리 된다.
  // render() 함수 위에서 state 선언
  state = {
    banana: "바나나",
  };
  render() {
    const { banana } = this.state;
    return (
      <div>
        <p>{banana}</p>
        <button
          onClick={() => {
            this.setState({ banana: "banana" });
          }}
        >
          영어로 변경! (Class 형)
        </button>
      </div>
    );
  }
}

Event Handling

💻 Synthethic Event (합성 이벤트)

export default function SyntheticEvent() {
  function syntheticEvent(e) {
    console.log(e);
    console.log("합성 이벤트 클릭");
  }

  function printInput(e) {
    console.log(e.target.value);
  }

  function callTest() {
    alert("안녕하세요?");
  }

  return (
    <div>
      <button onClick={syntheticEvent}>콘솔을 보세요</button>
      <button onClick={callTest()}>함수 호출해서 전달</button>
      <br />
      <input
        type="text"
        placeholder="글자를 입력 하세요."
        onChange={(e) => {
          printInput(e);
        }}
      />
    </div>
  );
}

map()

- 배열 데이터를 좀 더 효율적으로 사용하기 위해 map 사용!

import { useState } from "react";

export default function MapPrac() {
  const [list, setList] = useState([
    { id: "1", name: "코디", email: "codi@gmail.com" },
    { id: "2", name: "포터", email: "harrypotter@gmail.com" },
  ]);
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const addUser = () => {
    const newList = list.concat({
      id: list.length == 0 ? 1 : list[list.length - 1].id + 1,
      name: name,
      email: email,
    });
    setList(newList);
    setName("");
    setEmail("");
  };

  // 더블클릭 삭제
  const deleteUser = (id) => {
    const newUser = list.filter((name) => {
      return name.id !== id;
    });
    setList(newUser);
  };

  // 엔터로 입력
  const enterInput = (e) => {
    if (e.key == "Enter") {
      addUser();
    }
  };

  return (
    <>
      <input
        type="text"
        placeholder="이름"
        value={name}
        onChange={(e) => {
          setName(e.target.value);
        }}
      />
      <input
        type="text"
        placeholder="이메일"
        value={email}
        onDoubleClick={deleteUser}
        onChange={(e) => {
          setEmail(e.target.value);
        }}
        onKeyDown={enterInput}
      />
      <button onClick={addUser}>등록</button>
      <ul>
        {list.map((el) => {
          return (
            <li
              onDoubleClick={() => deleteUser(el.id)}
              style={{ fontWeight: 700 }}
            >
              {el.name}: {el.email}
            </li>
          );
        })}
      </ul>
    </>
  );
}

filter()

- filter()의 인자로 넘겨지는 callback함수의 테스트(조건)를 통과하는 요소를 모아 새로운 배열을 생성

- 배열에서 원하는 값을 삭제하는 코드 구현 가능

import { useState } from "react";

export default function Alphabet() {
  const [list, setList] = useState([
    { id: 1, alphabet: "a" },
    { id: 2, alphabet: "b" },
    { id: 3, alphabet: "c" },
    { id: 4, alphabet: "d" },
    { id: 5, alphabet: "e" },
  ]);
  const [input, setInput] = useState("");
  const addAlphabet = () => {
    const newList = list.concat({
      id: list.length == 0 ? 1 : list[list.length - 1].id + 1,
      alphabet: input,
    });
    setList(newList);
    setInput("");
  };
  // input 태그에 대고 엔터를 눌렀을 때 등록이 되도록
  const activeEnter = (e) => {
    // console.log(e.key); // 키보드의 정보
    if (e.key == "Enter") {
      addAlphabet();
    }
  };

  // 해당 태그에 더블클릭을 했을 때 삭제 되도록
  const deleteAlphabet = (id) => {
    const newAlphabet = list.filter((alphabet) => {
      return alphabet.id !== id;
    });
    setList(newAlphabet);
  };

  return (
    <div>
      <h2>alphabet</h2>
      <input
        type="text"
        value={input}
        onChange={(e) => {
          setInput(e.target.value);
        }}
        onKeyDown={activeEnter}
        onDoubleClick={deleteAlphabet}
      />
      <button onClick={addAlphabet}>추가</button>
      <ol>
        {list.map((el) => {
          return (
            <li key={el.id} onDoubleClick={() => deleteAlphabet(el.id)}>
              {el.alphabet}
            </li>
          );
        })}
      </ol>
    </div>
  );
}

Life Cycle

💻 생명주기

1. 생성(Mounting)

- constructor: 컴포넌트가 생성될 때 가장 먼저 실행되는 메서드로, 초기 state를 설정할 수 있다.

- render: 컴포넌트를 렌더링하는 가장 중요한 메서드

- componentDidMount: 컴포넌트가 처음 렌더링된 후 실행되며, DOM 조작이나 데이터 요청 등을 수행

2. 업데이트(Updating)

- getDerivedStateFromProps: props나 state의 변경을 감지하고 state를 업데이트

- shouldComponentUpdate: 컴포넌트의 리렌더링 여부를 결정

- render: 변경된 내용을 반영하여 다시 렌더링

- componentDidUpdate: 컴포넌트 업데이트가 완료된 후 실행

3. 제거(Unmounting)

- componentWillUnmount: 컴포넌트가 DOM에서 제거되기 전에 호출되며, 타이머 제거나 네트워크 요청 취소 등의 정리 작업을 수행

import { Component } from "react";

class MyComponent extends Component {
  // 마운트되었을 때 동작
  componentDidMount() {
    console.log("mount 되었어요!!🐛");
  }

  // 업데이트되었을 때 동작
  componentDidUpdate() {
    console.log("update 되었어요!!🔥");
  }

  // 언마운트 되기 직전
  componentWillUnmount() {
    console.log("unmount 됩니다!!✨");
  }
  render() {
    return <p>MyComponent {this.props.number}</p>;
  }
}

class LifeCycleClass extends Component {
  state = {
    number: 0,
    visible: true,
  };

  changeNumberState = () => {
    this.setState({ number: this.state.number + 1 });
  };
  changeVisibleState = () => {
    this.setState({ visible: !this.state.visible });
  };
  render() {
    return (
      <>
        <button onClick={this.changeNumberState}>PLUS</button>
        <button onClick={this.changeVisibleState}>On/Off</button>
        {/* 
        - visible state 값에 따라서 MyComponent 가 생성및 제거됨
        - 생성(mount) , 제거(unmount)
         */}
        {this.state.visible && <MyComponent number={this.state.number} />}
      </>
    );
  }
}

export default LifeCycleClass;

🤔 실습오답

🍎 Select.js

- 내용 부분이 안뜨는게 Result.js에서 {content}를 불러오지 않아서 였다.

더보기
// Select.js

export default function Select({ setData }) {
  /**
 * { fruit: "apple",
    background: "white",
    color: "gray",
    content: "text", }
 * 
 */

  return (
    <div>
      {/* {select 3개} */}
      과일:{" "}
      <select
        onChange={(e) => {
          // console.log("target", e.target);
          // console.log("current", e.currentTarget);
          // console.log("current", e.target.value);

          setData((prevState) => {
            return { ...prevState, fruit: e.target.value };
          });
        }}
      >
        <option value="apple">사과</option>
        <option value="grape">포도</option>
        <option value="peach">복숭아</option>
        <option value="banana">바나나</option>
      </select>
      배경색:
      <select
        onChange={(e) => {
          setData((prevState) => {
            return { ...prevState, background: e.target.value };
          });
        }}
      >
        <option value="black">black</option>
        <option value="white">white</option>
        <option value="red">red</option>
        <option value="blue">blue</option>
        <option value="green">green</option>
        <option value="yellow">yellow</option>
        <option value="pink">pink</option>
      </select>
      글자색:
      <select
        onChange={(e) => {
          const color = e.target.value;
          setData((prevState) => {
            return { ...prevState, color };
          });
        }}
      >
        <option value="black">black</option>
        <option value="white">white</option>
        <option value="red">red</option>
        <option value="blue">blue</option>
        <option value="green">green</option>
        <option value="yellow">yellow</option>
        <option value="pink">pink</option>
      </select>
    </div>
  );
}



// Result.js

export default function Result(props) {
  const { content, fruit, color, background } = props.data;
  //   console.log("data", data); // Object
  return (
    <div>
      <img src={`/${fruit}.jpg`} width={100} height={100} />
      {/* 백틱을 넣는 것 또한 JS 문법이므로 중괄호로 감싼다 */}
      <p
        style={{
          backgroundColor: background,
          color: color,
          width: "100px",
          height: "30px",
          textAlign: "center",
          lineHeight: "30px",
        }}
      >
        {content} << 이 부분이 문제였다.
      </p>
    </div>
  );
}

 

📓 MapPrac2.js

- 사소한 실수였다. button에 type을 넣지 않아서 내용을 입력하면 자동으로 새로고침이 되었었다.🥲

        <button type="button" onClick={addContent}>
          작성
        </button>
type="button" 을 안넣어서...
더보기
// MapPrac2.js

import { useState } from "react";

export default function MapPrac2() {
  const [comment, setComment] = useState([
    {
      writer: "승철",
      title: "화이팅",
    },
    {
      writer: "정한",
      title: "하니해",
    },
    {
      writer: "지수",
      title: "화이팅",
    },
  ]);
  const [inputTitle, setTitle] = useState(""); // 제목 등록 input
  const [inputWriter, setWriter] = useState(""); // 작성자 등록 input
  const [inputSearch, setInputSearch] = useState(""); // 검색어 input

  const [result, setResult] = useState([]); // 검색결과에 대한 배열
  const [searchType, setSearchType] = useState("writer");

  const addContent = () => {
    let newComment = {
      writer: inputWriter,
      title: inputTitle,
    };
    setComment([...comment, newComment]);

    setTitle("");
    setWriter("");
  };

  // 검색을 실행하는 함수
  const searchComment = () => {
    let searchResult = comment.filter((item) => {
      //   console.log(item);
      //   console.log(item[searchType].includes(inputSearch)); // includes -> 포함 여부를 T/F로 반환
      if (!item[searchType].includes(inputSearch)) {
        return null;
      }
      return item;
    });
    setResult(searchResult); // 검색어 결과 설정
    setInputSearch("");
  };

  // search type에 따라서 어떤 검색을 할지 결정
  const selectSearchType = (e) => {
    setSearchType(e.target.value);
  };

  return (
    <>
      <form>
        <label htmlFor="writer">작성자: </label>
        <input
          type="text"
          name="writer"
          id="writer"
          value={inputWriter}
          onChange={(e) => {
            setWriter(e.target.value);
          }}
        />
        {"  "}
        <label htmlFor="title">제목: </label>
        <input
          type="text"
          name="title"
          id="title"
          value={inputTitle}
          onChange={(e) => {
            setTitle(e.target.value);
          }}
        />
        {"  "}
        <button type="button" onClick={addContent}>
          작성
        </button>
      </form>
      {/* 검색 폼 */}
      <form>
        <select name="type" onChange={selectSearchType}>
          <option value={"writer"}>작성자</option>
          <option value={"title"}>제목</option>
        </select>{" "}
        <input
          type="text"
          onChange={(e) => {
            setInputSearch(e.target.value);
          }}
          placeholder="검색어를 입력 해 주세요."
          value={inputSearch}
          name="search"
        />{" "}
        <button type="button" onClick={searchComment}>
          검색
        </button>
      </form>
      <table border={1} style={{ margin: "30px auto", width: "500px" }}>
        <thead>
          <tr>
            <th>번호</th>
            <th>제목</th>
            <th>작성자</th>
          </tr>
        </thead>
        <tbody>
          {comment.map((value, idx) => {
            return (
              <tr key={idx + 1}>
                <td>{idx + 1}</td>
                <td>{value.title}</td>
                <td>{value.writer}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
      <h4>검색 결과</h4>
      {result.length == 0 ? (
        <h3>검색 결과가 없어요🥲</h3>
      ) : (
        <table border={1} style={{ width: "500px", margin: "0 auto" }}>
          <thead>
            <tr>
              <th>번호</th>
              <th>제목</th>
              <th>작성자</th>
            </tr>
          </thead>
          <tbody>
            {result.map((el, i) => {
              return (
                <tr key={i + 1}>
                  <td>{i + 1}</td>
                  <td>{el.title}</td>
                  <td>{el.writer}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      )}
    </>
  );
}