๐ŸŽฏ React ๊ด€๋ จ ๊ฐœ๋…(์ปดํฌ๋„ŒํŠธ, ์ƒํƒœ, ํ›… ๋“ฑ)์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“— Today I Learned

์ปดํฌ๋„ŒํŠธ

์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ UI(User Interface) ์กฐ๊ฐ์ž…๋‹ˆ๋‹ค.

React์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

โ„น๏ธ ์ปดํฌ๋„ŒํŠธ๋Š” ์ด๋ฆ„์„ ๋ฐ˜๋“œ์‹œ PascalCase๋กœ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์†Œ๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋ฉด <div>์ฒ˜๋Ÿผ HTML ํƒœ๊ทธ๋กœ ์ฐฉ๊ฐํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
์ถœ์ฒ˜: React ๊ณต์‹ ๋ฌธ์„œ



ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ

ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” ES6์˜ ํด๋ž˜์Šค ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑํ•˜๋ฉฐ, ๋ฐ˜๋“œ์‹œ render() ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ UI๋ฅผ JSX๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“„ ClassCom.jsx

import { Component } from 'react';

class ClassCom extends Component {
  render() {
    return <div>ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ</div>;
  }
}

export default ClassCom;

๐Ÿ“„ App.jsx

ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

import ClassCom from './ClassCom';

function App() {
  let name = '๋ฆฌ์•กํŠธ';

  return (
    <div>
      <ClassCom></ClassCom>
    </div>
  );
}

export default App;

ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ

ES6์˜ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋‚˜ ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•˜๋ฉฐ, return ๋ฌธ์œผ๋กœ JSX๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ตœ๊ทผ์—๋Š” React Hooks์˜ ๋“ฑ์žฅ์œผ๋กœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ‘œ์ค€์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“„ FuncCom.jsx

function FuncCom() {
  return <div>ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ</div>;
}

export default FuncCom;

๐Ÿ“„ App.jsx

ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

import FuncCom from './FuncCom';

function App() {
  return (
    <div>
      <FuncCom></FuncCom>
    </div>
  );
}

export default App;



state ์‚ฌ์šฉํ•˜๊ธฐ

React์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ state๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

useState๋Š” React์—์„œ ์ œ๊ณตํ•˜๋Š” ํ›…(hook) ์ค‘ ํ•˜๋‚˜๋กœ, ๊ฐ’์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ€์ˆ˜ ํ•˜๋‚˜์™€ ๊ทธ ๊ฐ’์„ ๋ฐ”๊พธ๋Š” ํ•จ์ˆ˜ ํ•˜๋‚˜๋ฅผ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•ด์ค๋‹ˆ๋‹ค.

๐Ÿค” ํ›…(Hook)์ด๋ž€?

Hook์€ React ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํŠน๋ณ„ํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. React์˜ ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜ ๋‚ด๋ถ€์— "๊ฑธ์–ด์ค€๋‹ค(hook into)"๋Š” ์˜๋ฏธ์—์„œ ๋ถ™์—ฌ์ง„ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.


useState ์‚ฌ์šฉ ์˜ˆ์ œ

import React, { useState } from 'react';

const Counter: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <p>ํ˜„์žฌ ์นด์šดํŠธ: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
};

export default Counter;
  • const [count, setCount] = useState<number>(0) : count๋Š” ํ˜„์žฌ ์ƒํƒœ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๊ณ , setCount๋Š” ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

    • state๊ฐ€ ๋ฐ”๋€Œ๋ฉด โ†’ React๊ฐ€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
  • onClick={() => setCount(count + 1)} : ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค count ๊ฐ’์„ 1์”ฉ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.


โš ๏ธ state๋Š” ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ , ๋ฐ˜๋“œ์‹œ setํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

count = count + 1 โŒ โ†’ setCount(count + 1) โœ…


[โœจ ์ถ”๊ฐ€ ๊ฐœ๋… ] setState ์—ฌ๋Ÿฌ ๋ฒˆ ์จ๋„ ์ ์šฉ๋˜์ง€ ์•Š๋Š” ์ด์œ 

React์—์„œ ์„ฑ๋Šฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์š”์ฒญ๋“ค์„ ํ์— ๋ชจ์•„๋‘๊ณ  ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— 1๋ฒˆ๋งŒ ์ ์šฉ์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

[์ฝ”๋“œ ์˜ˆ์‹œ]

ํ•ด๋‹น ์ฝ”๋“œ๋Š” +3์ด ๋  ๊ฑฐ ๊ฐ™์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” +1๋งŒ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

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

function handleClick() {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}

์ด๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด์ „ ์ƒํƒœ์—์„œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ˜•์‹์œผ๋กœ ๋ฐ”๊พธ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

function handleClick() {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);
}

๐Ÿ‘‰ ์ฐธ๊ณ  ๊ณต์‹ ๋ฌธ์„œ




ํ•  ์ผ ์ถ”๊ฐ€ ์ฝ”๋“œ ์˜ˆ์ œ

๐Ÿ“„ TodoList.jsx

import { useState } from 'react';
import { Button } from 'react-bootstrap';

type Todo = {
  id: number;
  text: string;
  isChecked: boolean;
};

const TodoList: React.FC = () => {
  const title: string = '์˜ค๋Š˜ ํ•  ์ผ';

  // ํ•  ์ผ ๋ชฉ๋ก ์ƒํƒœ๊ฐ’ (์ดˆ๊ธฐ๊ฐ’ 3๊ฐœ๋กœ ์‹œ์ž‘)
  const [todos, setTodos] = useState<Todo[]>([
    { id: 1, text: '๊ณต๋ถ€ํ•˜๊ธฐ', isChecked: false },
    { id: 2, text: '์ž ์ž๊ธฐ', isChecked: false },
    { id: 3, text: '๋ฏธํŒ…ํ•˜๊ธฐ', isChecked: false },
  ]);

  const [newTodo, setNewTodo] = useState<string>('');
  const [showDetail, setShowDetail] = useState<boolean>(false);
  const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);

  // ์ฒดํฌ๋ฐ•์Šค ์ƒํƒœ๋ฅผ ํ† ๊ธ€ํ•˜๋Š” ํ•จ์ˆ˜
  const handleCheckedChange = (itemId: number) => {
    setTodos((preItems) =>
      preItems.map((item) =>
        item.id === itemId ? { ...item, isChecked: !item.isChecked } : item
      )
    );
  };

  // ์ƒˆ ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜
  const addTodo = () => {
    if (newTodo.trim() !== '') {
      setTodos([
        ...todos,
        {
          id: Date.now(),
          text: newTodo,
          isChecked: false,
        },
      ]);
      setNewTodo('');
    }
  };

  // ํ•  ์ผ์„ ์‚ญ์ œํ•˜๋Š” ํ•จ์ˆ˜
  const removeTodo = (id: number) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  // ํ•  ์ผ ํด๋ฆญ ์‹œ ์ƒ์„ธ๋ณด๊ธฐ ์ƒํƒœ ์„ค์ •
  const handleTodoClick = (todo: Todo) => {
    setShowDetail(true);
    setSelectedTodo(todo);
  };
  
  // ์ƒ์„ธ๋ณด๊ธฐ ๋‹ซ๊ธฐ
  const handleCloseDetail = () => {
    setShowDetail(false);
  };

  return (
    <div>
      <h1>{title}</h1>
      <div className='container'>
        {/* ์ž…๋ ฅ์ฐฝ + ์ถ”๊ฐ€ ๋ฒ„ํŠผ */}
        <div>
          <input
            type='text'
            placeholder='ํ•  ์ผ ์ž…๋ ฅ'
            style={{ marginRight: '10px', writingMode: 'horizontal-tb' }}
            onChange={(e) => {
              setNewTodo(e.target.value);
            }}
          />
          <Button variant='success' onClick={addTodo}>
            ์ถ”๊ฐ€
          </Button>
        </div>
        {/* ํ•  ์ผ ๋ฆฌ์ŠคํŠธ */}
        <div className='board'>
          <ul>
            {todos.map((todo, index) => (
              <li key={todo.id}>
                <input
                  type='checkbox'
                  onChange={() => {
                    handleCheckedChange(todo.id);
                  }}
                />
                <span onClick={() => handleTodoClick(todo)}>
                  {todo.isChecked ? <del>{todo.text}</del> : todo.text}
                </span>
                <button
                  onClick={() => removeTodo(todo.id)}
                  className='delbutton'
                >
                  ์‚ญ์ œ
                </button>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
};

export default TodoList;
  • ํ•  ์ผ ์ฒดํฌ๋ฐ•์Šค(handleCheckedChange)

    • isChecked์˜ ๊ฐ’์„ ๋…ผ๋ฆฌ๊ฐ’์„ ๋ฐ˜์ „ํ•ด์ค๋‹ˆ๋‹ค.
  • ํ•  ์ผ ์ถ”๊ฐ€(addTodo)

    • ์ž…๋ ฅ๊ฐ’์ด ๋น„์–ด์žˆ์ง€ ์•Š์œผ๋ฉด, ์ƒˆ ํ•ญ๋ชฉ์„ todos ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค

    • trim()์„ ํ†ตํ•ด ๊ณต๋ฐฑ๋งŒ ์ž…๋ ฅ๋œ ๊ฒฝ์šฐ๊นŒ์ง€ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒ์„ธ๋ณด๊ธฐ(showDetail/ selecyedTodo) ์ถ”ํ›„์— ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.



react-bootstrap

๐Ÿ‘‰ ๋ฆฌ์•กํŠธ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ getting started


์„ค์น˜ ๋ฐฉ๋ฒ•

1๏ธโƒฃ npm์œผ๋กœ react-bootstrap์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

npm install react-bootstrap bootstrap

2๏ธโƒฃ index.tsx ํŒŒ์ผ์— css๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค

import 'bootstrap/dist/css/bootstrap.min.css';

๋˜๋Š” CDN์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
  integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
  crossorigin="anonymous"
/>

์‚ฌ์šฉ ์˜ˆ์ œ

import { Form, Button } from 'react-bootstrap';

<Form>
  <Form.Group className="mb-3" controlId="todoInput">
    <Form.Label>ํ•  ์ผ ์ž…๋ ฅ</Form.Label>
    <Form.Control type="text" placeholder="ํ•  ์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”" />
  </Form.Group>
  <Button variant="success">์ถ”๊ฐ€</Button>
</Form>



โœ๏ธ ํšŒ๊ณ 

React์˜ ํ›…์— ๋Œ€ํ•ด์„œ ์•„์ง์€ ๋‚ฏ์„ค์–ด์„œ ๊ณ„์† ์ ์–ด๋ณด๋ฉด์„œ ์—ฐ์Šตํ•ด์•ผ๊ฒ ๋‹ค.

profile
๐ŸŒฑ๊ฐœ๋ฐœ ๊ธฐ๋ก์žฅ

0๊ฐœ์˜ ๋Œ“๊ธ€