지선생님의 커리큘럼 따라가기 다음단계를 해보겠읍니다.

 

 

지난번까지는 TodoList만들기를 해봤는데, 이제는 여러 페이지를 연결하는 법을 알려준대여.

라우팅을 하는법을 알려준다는데 라우팅은 url에 해당하는 페이지로 이동시켜주는 그런 느낌인듯.

 

리액트 라우터를 설치하래여. 즉시.

 

설치하고 나서는 라우터를 설정하래요

 

App.js를 아래처럼 수정해줍니다.

 

// src/App.js
import React, {useState} from 'react';
import {BrowerRouter as Router, Routes, Route, Link} from 'react-router-dom';
import TodoListPage from './pages/TodoListPage';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

function App() {
  return(
    <Router>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/todos">Todo List</Link></li>
          <li><Link to="/about">About</Link></li>
        </ul>
      </nav>

      <Routes>
        <Route path="/" element={<HomePage/>}/>
        <Route path="/todos" element={<TodoListPage/>}/>
        <Route path="/about" element={<AboutPage/>}/>
      </Routes>
    </Router>
  );
}

export default App;

 

 

  • BrowserRouter: SPA의 라우팅을 지원합니다.
  • Routes와 Route: 각각의 경로에 컴포넌트를 연결합니다.
  • Link: 페이지 간 탐색을 위한 링크를 생성합니다.

지선생님이 저렇대요.

BrowserRouter를 통해 라우팅을 하도록 하고,

Routes는 여러 Route를 감싸는것 같아요.

Link는 그냥 <a>태그 느낌인듯

 

이제 다음으로 각 페이지 컴포넌트를 만들거에요.

 

src폴더 밑에 pages라는 폴더를 하나 만들고 거기다 컴포넌트 3개를 만들어서 넣어줄겁니당.

 

첫 번째로

HomePage.js 를 만들어줍시다. 코드는 아래와 같습니다.

import React from 'react';

function HomePage() {
  return (
    <div>
      <h1>Welcome to the Todo App!</h1>
      <p>This is the home page of our simple React app.</p>
    </div>
  );
}

export default HomePage;

 

두 번째로

TodoListPage.js 를 만들어줄게요. 코드는 아래와 같습니다.

import React, { useState } from 'react';
import TodoList from '../TodoList';
import TodoForm from '../TodoForm';

function TodoListPage() {
  const [todos, setTodos] = useState([]);

  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(),
      text,
      completed: false,
    };
    setTodos([...todos, newTodo]);
  };

  const toggleTodo = (id) => {
    setTodos(
      todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const removeTodo = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  return (
    <div>
      <h1>Todo List</h1>
      <TodoForm addTodo={addTodo} />
      <TodoList todos={todos} toggleTodo={toggleTodo} removeTodo={removeTodo} />
    </div>
  );
}

export default TodoListPage;

 

전에 작성했던 코드를 거의 그대로 옮겨주기만 하면 대여

 

이제 마지막

AboutPage.js 코드는 아래와 같습니다.

import React from 'react';

function AboutPage() {
  return (
    <div>
      <h1>About This App</h1>
      <p>This app is built using React. It allows you to manage your tasks efficiently.</p>
    </div>
  );
}

export default AboutPage;

 

홈페이지랑 어바웃페이지는 그냥 머 없이 페이지만 그려서 넘겨주고 투두리스트페이지에 기능이 들어가네여.

 

이제 즉시 실행해서 확인해보기.

 

일단 시작페이지는 위와 같네여

 

Home을 누르면 그냥 위 페이지가 보여여

Todo List를 누르면?

 

전에 만들었던 투두리스트가 나오고 추가할수 있어요.

 

About을 누르면

이렇게 어바웃 페이지가 나온답니다.

근데 다시 Todo List로 가면 내가 추가한거 사라져잇음..

다시 유지하려면 어떻게하지? 는 지피티가 언젠가 알려주겟죠? 그냥 지선생 믿어 그냥

 

이제 다음!

지선생님이 동적라우팅이랑 API연동을 알려준대여

일단 동적라우팅까지만 알아봅시당

 

 

/todos/1 처럼 이동햇을때 보여줄 수 있도록 해준다는것 같아요.

 

App.js 를 아래처럼 수정해줍시다.

import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import TodoListPage from './pages/TodoListPage';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import TodoDetailsPage from './pages/TodoDetailsPage';

function App() {
  return (
    <Router>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/todos">Todo List</Link></li>
          <li><Link to="/about">About</Link></li>
        </ul>
      </nav>

      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/todos" element={<TodoListPage />} />
        <Route path="/todos/:id" element={<TodoDetailsPage />} />
        <Route path="/about" element={<AboutPage />} />
      </Routes>
    </Router>
  );
}

export default App;

 

 

import TodoDetailsPage from './pages/TodoDetailsPage';

...
<Route path="/todos/:id" element={<TodoListPage/>}/>

 

위 두 줄이 추가됐어요.

그럼 이제 TodoDetailsPage를 만들어야겠져?

 

TodoDetailsPage.js 코드는 아래와 같습니당

import React from 'react';
import { useParams } from 'react-router-dom';

function TodoDetailsPage() {
  const { id } = useParams();

  return (
    <div>
      <h1>Todo Details</h1>
      <p>This is the details page for Todo ID: {id}</p>
    </div>
  );
}

export default TodoDetailsPage;

 

useParams: URL 매개변수를 읽을 수 있는 React Router 훅입니다.

useParams이라는 새로운 친구가 나타났는데 URL에서 매개변수를 읽을 수 있대여

 

App.js에서 /todos/:id 로 라우팅을 해줬는데 id를 읽는것 같아요.

 

이제 Todo 항목을 클릭햇을때 이동할 수 잇도록

TodoItem도 수정해줍니다.

 

코드는 아래와 같습니다.

TodoItem.js

import React from 'react';
import { Link } from 'react-router-dom';

function TodoItem({ todo, toggleTodo, removeTodo }) {
  return (
    <li style={{ display: 'flex', alignItems: 'center' }}>
      <Link to={`/todos/${todo.id}`} style={{ flex: 1, textDecoration: 'none', color: 'black' }}>
        <span
          style={{
            textDecoration: todo.completed ? 'line-through' : 'none',
            cursor: 'pointer',
          }}
          onClick={(e) => {
            e.stopPropagation(); // 링크 클릭 시 toggleTodo 방지
            toggleTodo(todo.id);
          }}
        >
          {todo.text}
        </span>
      </Link>
      <button onClick={() => removeTodo(todo.id)} style={{ marginLeft: '10px' }}>
        Delete
      </button>
    </li>
  );
}

export default TodoItem;

 

Link로 감싸서 눌릴 수 있도록 하고, /todos/{id}로 이동할 수 있도록 수정했습니다.

그리고 이제 TodoList 페이지에서 아무거나 추가하고 제목을 클릭하면!!

 

url에서 id에 해당하는 부분을 갖고와서 사용할 수 있습니다.

아주좋습니다.

 

라우팅하는 방법, 동적 라우팅 하는 방법에 대해 잘 배웠네요.

 

다음엔 API연동하는 방법을 알아보겠읍니다.

 

 

끝!

 

 

이번엔 지선생님의 가르침에 따라 TodoList 만들기를 해보겠습니다.

 

 

저런 기능을 만든다고 하고,

저런 컴포넌트를 만든다고 합니다.

 

먼저 App.js부터!

 

 

TodoList, TodoItem, TodoForm은 아직 없지만 일단 시키는대로 써보기!

 

App.js

// src/App.js
import React, {useState} from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';

function App() {

  const [todos, setTodos] = useState([]);

  const addTodo = (text) => {
    const newTodo = {
      id : Date.now(),
      text,
      completed : false,
    };
    setTodos([...todos, newTodo]);
  };

  return (
    <div className="App">
      <h1>Todo List</h1>
      <TodoForm addTodo = {addTodo}/>
      <TodoList todos = {todos}/>
    </div>
  );
}

export default App;

 

이렇게 쓰래용 써봤습니다.

여기서 addTodo 함수는 새로운 Todo 항목을 만들어 todos 상태에 추가하는 역할을 합니다. TodoForm 컴포넌트에 addTodo 함수를 전달해 사용자가 새 항목을 추가할 수 있도록 할 거예요.

 

 

이제 TodoForm을 만들어보겠습니다.

 

TodoForm.js

import React, {useState} from 'react';

export default function TodoForm({addTodo}){
    const [input, setInput] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        if (input.trim()){
            addTodo(input);
            setInput('');
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
            type="text"
            value = {input}
            onChange = {(e) => setInput(e.target.value)}
            placeholder ="Enter new task"
            />
        <button type = "submit">Add Todo</button>
        </form>
    );
}

 

useState를 통해 input값을 바꾸고, handleSubmit 함수를 이용해서 submit을 하는것 같아요.

 

 

다음으로 TodoList 컴포넌트 만들기!

 

import React, {useState} from 'react';
import TodoItem from './TodoItem';

function TodoList({todos}){
    return(
        <ul>
            {todos.map((todo) => (
                <TodoItem key = {todo.id} todo = {todo}/>
            ))}
        </ul>
    )
}

export default TodoList;

 

생각보다 간단한데..?

TodoItem 컴포넌트를 자식 컴포넌트로 갖고있네요.

TodoItem도 만들어봅시다!

 

TodoItem.js

import React from 'react';

export default function TodoItem({todo}){
    return(
        <li style= {{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
            {todo.text}
        </li>
    );
}

 

오.. props로 전달받은  todo에서 textDecoration 속성을 todo.completed에 따라 선을 그을건지, 아무것도 안할건지 정하는것 같아요.

App.js에서 처음에 todo.completed 값을 false로 했으니 추가하면 선이 안그어져있는 상태일것 같습니다.

 

이제 서버를 키고 실행해보면?

 

 

이런 화면을 볼 수 있어요

그리고 추가해보면???

 

 

끝내주게 숨쉬기

작삭나게 밥먹기

간지나게 자기

를 추가해봤어요. 자기 안자기.

 

 

지금만든건 추가만 되고, 삭제나 완료표시를 못하는데 이제 추가, 완료 기능을 추가한다고 합니다.

 

즉시 해봐

 

 

완료 상태 토글 기능을 추가할거에요.

App.js를 아래처럼 작성해봅시다.

 

App.js

// src/App.js
import React, { useState } from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';

function App() {
  const [todos, setTodos] = useState([]);

  // 새로운 Todo 항목 추가
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(), // 고유한 ID
      text,
      completed: false,
    };
    setTodos([...todos, newTodo]);
  };

  // Todo 완료 상태 토글
  const toggleTodo = (id) => {
    setTodos(
      todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  return (
    <div className="App">
      <h1>Todo List</h1>
      <TodoForm addTodo={addTodo} />
      <TodoList todos={todos} toggleTodo={toggleTodo} />
    </div>
  );
}

export default App;

 

toggleTodo 함수를 추가하고

TodoList에 toggleTodo 함수를 전달해주는 코드가 추가됐어요.

 

이제? 다음단계

 

 

TodoList.js 도 아래처럼 수정합시다.

 

// src/TodoList.js
import React from 'react';
import TodoItem from './TodoItem';

function TodoList({ todos, toggleTodo }) {
  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
}

export default TodoList;

 

여기서도 인자로 toggleTodo를 받고, TodoItme에 toggleTodo를 전달해주는게 추가됐어요.

 

그리고 TodoItem.js도 수정해봅시다.

 

TodoItem.js

// src/TodoItem.js
import React from 'react';

function TodoItem({ todo, toggleTodo }) {
  return (
    <li
      style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer' }}
      onClick={() => toggleTodo(todo.id)}
    >
      {todo.text}
    </li>
  );
}

export default TodoItem;

 

TodoItem에서 onClick 이벤트를 처리하여 Todo 완료 상태를 토글해여.

 

이제 Todo 항목을 클릭하면 완료 상태가 토글되고, 완료된 항목은 텍스트가 취소선으로 표시된대여.

 

실행하고 항목 추가 후 항목을 클릭해보면?

 

오오오오오 된다.ㅋㅋㅋㅋ

다시 클릭하면 취소선이 사라져요.

 

이제 마지막! 삭제기능 만들기!

 

 

App.js에 삭제함수를 추가해볼게여.

 

아래 함수를 추가해주세여

const removeTodo = (id) => {
  setTodos(todos.filter((todo) => todo.id !== id));
};

 

id를 인자로 받으면 todos배열에서 filter함수를 이용해 id가 같지 않은것들로 바꾸는 로직인것 같네여.

이제 함수를 만들었으니 TodoList에 전달해줄게여

<TodoList todos={todos} toggleTodo={toggleTodo} removeTodo={removeTodo} />

이렇게 써줍시다

 

전체 코드는 아래와 같아여

// src/App.js
import React, {useState} from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';

function App() {

  const [todos, setTodos] = useState([]);

  const addTodo = (text) => {
    const newTodo = {
      id : Date.now(),
      text,
      completed : false,
    };
    setTodos([...todos, newTodo]);
  };

  const toggleTodo = (id) => {
    setTodos(
      todos.map((todo) => 
      todo.id === id ? {...todo, completed : !todo.completed}: todo
      )
    );
  };

  const removeTodo = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  return (
    <div className="App">
      <h1>Todo List</h1>
      <TodoForm addTodo = {addTodo}/>
      <TodoList todos = {todos} toggleTodo = {toggleTodo} removeTodo = {removeTodo}/>
    </div>
  );
}

export default App;

 

 

다음으로

 

TodoList.js 수정!

 

removeTodo를 TodoItem에 전달해조여

 

TodoList 컴포넌트 부분을 아래처럼 바꿔줍니다.

 

function TodoList({ todos, toggleTodo, removeTodo }) {
  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          toggleTodo={toggleTodo}
          removeTodo={removeTodo}
        />
      ))}
    </ul>
  );
}

 

TodoItem에 removeTodo를 전달해줬으니 받아줘야겠죠?

 

TodoItem을 아래처럼 수정합니다.

function TodoItem({ todo, toggleTodo, removeTodo }) {
  return (
    <li style={{ display: 'flex', alignItems: 'center' }}>
      <span
        style={{
          textDecoration: todo.completed ? 'line-through' : 'none',
          cursor: 'pointer',
          flex: 1,
        }}
        onClick={() => toggleTodo(todo.id)}
      >
        {todo.text}
      </span>
      <button onClick={() => removeTodo(todo.id)} style={{ marginLeft: '10px' }}>
        Delete
      </button>
    </li>
  );
}

 

flex뭔디 왜 갑자기 추가하는건디

css는 일단 따라합시다.

 

일단 따라하고 실행해보면...

 

 

delete 버튼이 너무 끝에있는데요 선생님...? 다 뜻이 있겠죠 머

길게써도..잘 되네요...

 

삭제버튼 누르면 삭제도 잘 됩니다 일단은..

 

스타일이 마음에는 안드는데...아무튼 잘 되시잖아.

 

이렇게 TodoList 간단하게? 만드는것 까지 해봤어여. 다음엔 React Router로 여러 페이지를 연결하는 방법을 알려준다고 하네여.

 

일단 오늘은 이까지~

 

지선생님과 함께 상태와 이벤트 처리까지 배워봤습니다.

 

이번엔 렌더링과 key, 이벤트 처리 심화, 폼 제출 이벤트와 데이터 검증 에 대해 배워볼거에요.

 

 

- 렌더링과 키

 

리스트 렌더링과 key속성에 대해 알아본다고 하네여.

 

코드는 아래와 같습니다.

import React, { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState(['Learn React', 'Build a Project', 'Have Fun']);
  const [newTodo, setNewTodo] = useState('');

  // 새로운 할 일 추가 함수
  const addTodo = () => {
    if (newTodo.trim() !== '') {
      setTodos([...todos, newTodo]);
      setNewTodo(''); // 입력 필드 초기화
    }
  };

  return (
    <div>
      <h1>Todo List</h1>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo}</li>
        ))}
      </ul>
      <input
        type="text"
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
        placeholder="Enter new task"
      />
      <button onClick={addTodo}>Add Todo</button>
    </div>
  );
}

export default TodoList;

 

  • todos는 할 일 목록을 저장하는 배열 상태에요.
  • newTodo는 새로운 할 일 항목을 임시로 저장하는 상태에요.
  • addTodo 함수는 newTodo값이 공백이 아닐 때만 todos 배열에 추가하고, 입력 필드를 초기화해요.

map()을 사용해 todos배열의 각 항목을 <li> 요소로 렌더링 하고 있어요.

각 <li>에 key 속성으로 index를 부여하여 리액트가 각 항목을 고유하게 식별할 수 있도록 했다고 하네여.

 

이제 TodoList컴포넌트를 App.js에 추가해줍니다.

 

코드

// src/App.js
import React from 'react';
import TodoList from './TodoList';

function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}

export default App;

 

서버를 실행하고 들어가보면?

 

이제 하나를 추가해볼게여.

 

잘되는데? 좋은데~?

이런식으로 key속성은 리액트가 각 리스트 항목을 효율적으로 업데이트 할 수 있도록 도와준다고 해요.

실제로는 배열의 인덱스보다는 고유한 ID를 key로 사용하는 것이 더 좋다고 하네요 지선생님이.

인덱스를 사용하면 리스트 항목의 순서가 바뀌거나 삭제될 때 예기치 않은 문제가 발생할 수 있기 때문이라고 합니다.

 

이제 다음은 이벤트 처리 심화!


 

- 이벤트 처리 심화

 

이번에는 이벤트 처리를 통해 텍스트 입력받고, 체크박스, 드롭다운 이런거 관리한다고 하네요.

 

바로 해봐 그냥

 

 

코드

import React, { useState } from 'react';

function FormExample() {
  const [text, setText] = useState('');
  const [isChecked, setIsChecked] = useState(false);
  const [selectedOption, setSelectedOption] = useState('option1');

  // 입력 필드 이벤트 처리
  const handleTextChange = (e) => {
    setText(e.target.value);
  };

  // 체크박스 이벤트 처리
  const handleCheckboxChange = (e) => {
    setIsChecked(e.target.checked);
  };

  // 드롭다운 메뉴 이벤트 처리
  const handleSelectChange = (e) => {
    setSelectedOption(e.target.value);
  };

  return (
    <div>
      <h1>Form Example</h1>
      
      {/* 텍스트 입력 필드 */}
      <label>
        Text:
        <input type="text" value={text} onChange={handleTextChange} />
      </label>
      <p>Entered Text: {text}</p>

      {/* 체크박스 */}
      <label>
        <input type="checkbox" checked={isChecked} onChange={handleCheckboxChange} />
        Accept Terms
      </label>
      <p>Checkbox is {isChecked ? 'checked' : 'unchecked'}</p>

      {/* 드롭다운 메뉴 */}
      <label>
        Select Option:
        <select value={selectedOption} onChange={handleSelectChange}>
          <option value="option1">Option 1</option>
          <option value="option2">Option 2</option>
          <option value="option3">Option 3</option>
        </select>
      </label>
      <p>Selected Option: {selectedOption}</p>
    </div>
  );
}

export default FormExample;

 

  • 텍스트 입력 필드 : onChange 이벤트 핸들러 handleTextChange로 입력된 값을 text 상태에 업데이트한대여
  • 체크박스 : onChange 이벤트 핸들러 handleCheckboxChange로 체크 여부를 isChecked 상태에 저장한대여.
  • 드롭다운 메뉴 : onChange 이벤트 핸들러 handleSelectChange로 선택된 옵션을 selectedOption 상태에 업데이트 한대여.

App에 추가하고 확인해볼게여.

// src/App.js
import React from 'react';
import FormExample from './FormExample';

function App() {
  return (
    <div className="App">
      <FormExample />
    </div>
  );
}

export default App;

 

 

 

이제 텍스트 입력하고, 체크박스 체크하고, 드롭다운을 바꾸면?

각 요소의 밑에 상태를 표현해주는 곳도 바뀔거에요.

 

잘 되는데? 신기하네여.

 

이렇게 실시간으로 업데이트가 되나봐여. 잘 써먹어야 할텐데...

 

이제 오늘거 마지막! 폼 제출 이벤트와 데이터 검증을 해볼거에요.

 


 

- 폼 제출 이벤트 및 데이터 검증

 

간단하게 폼을 만들고 유효성검사? 하는걸 해보나봐요.

 

일단 해봐. 즉시.

 

코드

import React, { useState } from 'react';

function FormWithValidation() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');

  // 폼 제출 이벤트 핸들러
  const handleSubmit = (e) => {
    e.preventDefault(); // 폼의 기본 제출 동작을 막음

    // 유효성 검사
    if (name.trim() === '') {
      setError('Name is required.');
      return;
    }
    if (!email.includes('@')) {
      setError('Email must be valid.');
      return;
    }

    // 에러가 없으면 성공 메시지 표시
    setError('');
    alert(`Form submitted!\nName: ${name}\nEmail: ${email}`);
  };

  return (
    <div>
      <h1>Form with Validation</h1>
      <form onSubmit={handleSubmit}>
        {/* 이름 입력 */}
        <label>
          Name:
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </label>
        <br />

        {/* 이메일 입력 */}
        <label>
          Email:
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </label>
        <br />

        {/* 에러 메시지 */}
        {error && <p style={{ color: 'red' }}>{error}</p>}

        {/* 제출 버튼 */}
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

export default FormWithValidation;

 

  • handleSubmit : e.preventDefault()로 폼의 기본 제출 동작을 막고 유효성 검사를 하는 함수래여.
  • 유효성 검사 : 이름이 비어있거나, 이메일에 @가 포함되지 않으면 에러메시지를 출력한다고 하네여.
  • 에러 메시지 : error 상태에 에러 메시지를 저장하고, 이것을 화면에 표시해 사용자에게 알려준대여.

아 즉시 확인해봐

 

// src/App.js
import React from 'react';
import FormWithValidation from './FormWithValidation';

function App() {
  return (
    <div className="App">
      <FormWithValidation />
    </div>
  );
}

export default App;

 

 

 

 

이렇게 하고 예외상황을 만들어 볼게여

 

 

왼쪽은 아무것도 입력 안한건데 이름 먼저 체크를 해서 이름이 없다고 하네여.

오른쪽은 골뱅이 안넣어보니까 저렇게 잘 체크해주네여

 

이렇게 간단하게? 폼 제출과 유효성 검증을 해봤어요.

사용자 입력을 제어하고 검증하는 방법을 아주 간단하게 배워봤습니다.

이걸 좀 더 발전시키면 더 복잡한 폼이나 데이터 검증 로직도 구현할 수 있다고 하네여.

 

이제 다음 주제는 

 

 

 

이런걸 해보라고 하네여 지선생님이 !

 

그래서 그냥 하나하나 다 해보자고 했습니다.

 

째깐하게 만들었던 TodoList를 좀 더 고도화 하는것을 다음 포스트에 해보겠습니다!!

 

오늘은 끝.

 

 

이번에는 상태와 이벤트 처리를 배워봤어요.

 

 

이해됐으면 대답하라길래

 

응 잘 따라왔어.

 

냅다 반말했더니 좋대요.

 

이제 State와 이벤트 처리를 알려주겠대요.

 

State는 컴포넌트의 동적인 데이터를 관리하는 데 사용된대요. 상태가 변경되면 React는 자동으로 UI를 업데이트 해준다네요.

카운터 컴포넌트를 만들어보재요. 

 

즉시.

 

 

훅이 뭔데. 설명은 하고 말해주지...

그래도 일딴 따라해봤습니다.

 

코드는 아래와 같아요.

import React, { useState } from 'react';

function Counter() {
  // useState 훅을 사용하여 count 상태를 정의
  const [count, setCount] = useState(0);

  // 카운터 증가 함수
  const increase = () => setCount(count + 1);

  // 카운터 감소 함수
  const decrease = () => setCount(count - 1);

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={increase}>Increase</button>
      <button onClick={decrease}>Decrease</button>
    </div>
  );
}

export default Counter;

 

useState라는걸 사용해서 count라는 상태와 이를 업데이트 할 수 있는 함수를 정의한다고 합니다.

useState(0)으로 해서 count의 초기값을 0으로 설정한대요. 다른 값으로 바꾸면 그게 초기값으로 바뀌나봐요.

 

그래서 이제 increase함수와 decrease 함수를 정의하고 각각 함수를 호출하면 setCount 함수(?)를 통해 count를 어떻게 바꿀건지 정의했습니다.

 

이제 App.js에서 Counter 컴포넌트를 써볼게요.

 

App.js코드는 아래와 같습니다.

 

import React from 'react';
import Greeting from './Greeting';
import Counter from './Counter';

function App() {
  return (
    <div className="App">
      <h1>Hello, React World!</h1>
      <Greeting />
      <Counter />
    </div>
  );
}

export default App;

 

이제 실행해보면?

 

 

 

Increase를 누르면 Counter 옆의 숫자가 증가하고 Decrease를 누르면 감소해요.

페이지 리로딩없이 이렇게 되는게 신기한것 같아요.

근데 숫자는 어디까지 표현되는거지

 

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

 

한 번 다른언어(Java, C++) 정수형 최대치로 바꿔봤어요. 그리고 Increase를 눌러봤는데?

 

모야. 외올라가.

 

그래서 long 최대치로 바꿔봤어요

9223372036854775807

 

그랬더니?

 

엥? 모야. 뭔가 더커짐 그냥.

그리고 Increase, Decrease 버튼을 눌러도 아무런 변화가 없어요 숫자가.

이건 뭔가 자바스크립트에 대한 이해도가 필요해 보이네요.

나중에 알아보는거로 하고. 일단 넘어가기.

 

 

다음으로 Props에 대해 알아보재요

이제 컴포넌트간 데이터를 전달하는 방법을 배울 것 같습니다.

 

지선생님이 시키는대로 Welcome 컴포넌트를 만들어주고,

 

이렇게 App.js에 똑같이 추가해주는데?

name = " " 속성을 추가해주네요. 아마 저기 name에 대해 써주면 Welcome 컴포넌트의 props에 전달이 되는 구조인것 같아요.

이상태로 실행해보면, 

 

 

이렇게 되는데, 궁금한 점이 생겼습니다. 속성으로 전달되는걸 내맘대로 추가해도 되나? 이름도?

라는 생각에

 

asd라는 속성을 추가해봤어요. 그리고 Welcome 컴포넌트내용을

 

props.asd 라고 해봤더니?

 

 

오...이게되네...?

 

props라는건 컴포넌트를 호출하면서 함께 써준 속성들의 값을 호출당한(자식)컴포넌트에 전달 해 주는것 같아요.

 

 

다음 단계중 하나를 고르라길래 다하자고 했습니다.

 

이제, props와 State를 동시에 사용해보는걸 해볼겁니다.

 

 

Props와 State를 함께 사용해서, 자식컴포넌트에서 전달받은 데이터를 바꿔서 부모컴포넌트의 상태를 업데이트 한다고 하네요.

어떻게하지...? 

 

 

코드는 아래와 같습니다.

import React, { useState } from 'react';
import CounterChild from './CounterChild';

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

  const increaseCount = () => setCount(count + 1);
  const decreaseCount = () => setCount(count - 1);

  return (
    <div>
      <h1>Counter Parent</h1>
      <p>Current Count: {count}</p>
      {/* count와 상태 업데이트 함수를 자식 컴포넌트에 전달 */}
      <CounterChild count={count} onIncrease={increaseCount} onDecrease={decreaseCount} />
    </div>
  );
}

export default CounterParent;

 

부모 컴포넌트를 만들었어요. 그리고 자식 컴포넌트를 부르는데, 속성으로 count라는 변수와, onIncrease, onDecrease라는 이름을 가진, count를 조작하는 함수를 넘겨주는것 같습니다.

 

코드는 아래와 같습니다.

import React from 'react';

function CounterChild({ count, onIncrease, onDecrease }) {
  return (
    <div>
      <h2>Counter Child</h2>
      <p>Received Count: {count}</p>
      <button onClick={onIncrease}>Increase</button>
      <button onClick={onDecrease}>Decrease</button>
    </div>
  );
}

export default CounterChild;

 

자식컴포넌트에서는 props로 전달받은 데이터, 함수를 이용해주는것 같습니다.

아마 count는 부모로부터 받은 데이터이고, count를 조작하는 함수 또한 부모가 가진 데이터를 조작하는 것이라서,

Increase, Decrease 버튼을 클릭하면 부모 컴포넌트와 자식 컴포넌트에 보여지는 count가 동시에 증가하거나 감소할 것 같습니다.

 

즉시 해보기

 

 

이렇게 해보래여

 

해보고, 증가 감소버튼을 누르면,  예상한 것처럼 위 아래의 숫자가 동시에 바뀌는걸 볼 수 있습니다. 신기하다.

 

이렇게 부모-자식간의 데이터를 전달하고, 자식컴포넌트에서 부모컴포넌트의 데이터 상태를? 바꿀 수 있는 법도 배웠네요.

 

아직까지는 재밌는데?

 

이제 다음에는 리스트 랜더링과 키(key)에 대해 알려준대요.

 

이번 글은 여기까지!!

 

 

이제 GPT선생님을 믿고! 본격적으로 리액트에 대해 배워봅니다.

 

지선생님에게 선생님이 제시한 로드맵을 시작해보자고 했습니다.

 

 

리액트는 화면을 다양한 컴포넌트로 나누고, 컴포넌트 별로 관리하는것 같습니다.

 

그리고 JSX라는 것을 쓴다네요 JS안에서 HTML을 작성하는 것처럼 보이는 문법..?이라네요

뭔지는 모르겠지만 일단 그런갑다 하고 넘어갈게요. 

 

그리고 State와 Props가 있대요 데이터를 관리하고 변하면 자동으로 업데이트 해주나봐요 신기하네.

 

Props를 통해서 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달한대요

 

여기까지 혼자서 이해한걸 잠깐 정리해보면.

일단 컴포넌트로 화면과 데이터를 관리하는데, 컴포넌트 안에서 다른 컴포넌트를 계층적으로 쓸 수 있는것 같고.

계층적으로 쓰기 때문에 부모-자식 관계가 만들어지는것 같아요 트리처럼.

그래서 부모 자식간에 데이터를 Props를 통해 전달하나봐요.

그리고 상태를 관리하는? State를 통해 데이터가 변하면 알아서(?) 바뀔 수 있게 해주나봐요.

 

자세한건 일단 나중에 알아보고 시키는대로 해봅시다.

 

 

개발환경을 설정하래요.

Node랑 npm은 깔려있었어서 그냥 냅다 VsCode에 폴더 하나 만들었습니다.

그리고 vite 머시기가 뭔지 모르겠어서

npx create-react-app my-app

해서 리액트 프로젝트를 하나 만들었어요.

 

 

프로젝트가 생겼어요. 신기하다. 저게 알아서 생기네 그냥.

 

그리고 지선생님 말을 따르지 않았으니 따르지 않았다고 말해봤습니다.

 

 

혼나지는 않았고 좋다고 하네요.

 

public/  패키지에는 정적 파일이 들어가나봐요. index.html이 아래와 같은 코드처럼 써잇어요

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

 

이 파일에서 <div id="root"></div> 요소가 React 애플리케이션이 렌더링될 위치래요.

 

그리고 src/ 폴더 밑에가 실제 리액트 코드가 작성되는 폴더래요.

 

index.js가 애플리케이션 진입점인 파일이래요. ReactDOM을 사용해 App 컴포넌트를 위에있는 index.html에 랜더링한대요

 

App.js랑 App.css는 기본제공 파일이래요.

 

아하! 완벽히 이해했어!(이해 못함) 

 

뭔지 모르겠지만 일단 즉시 해봐

 

 

App.js를 저렇게 수정해보래요.

 

즉시

 

오. 됐다. 신기하다. 다음!

 

 

컴포넌트를 나누래요. 리액트의 주요개념이래요 컴포넌트가. 큰 UI작은 컴포넌트로 나누기.

약간 JAVA에서 객체지향 그런거랑 비슷한 느낌인가봐요.

 

Greeting 컴포넌트를 만들어봤습니다.

 

import React from 'react';

function Greeting(){
    return <p>나도 리액트 쓸 줄 안다 !</p>;
}

export default Greeting;

 

import React from 'react'; 를 해줘야 리액트러처럼 쓸 수 있나봐요.

 

그리고 export default Greeting; 을 해주는데 아마 function 을 바깥에서? 쓸 수 있게 해주는것 같아요.

 

 

이제 얘를 App.js에서 사용할 수 있게 해준대요

import Greeting from './Greeting'; 을 해주네요.

./ 은 App.js가 들어있는 폴더 내에서 찾는거같은데, Greeting.js가 아니라 Greeting만 해줘도 되나봐요.

 

저러고 이제 <Greeting /> 을 해주면?

 

 

 

오 나도 리액트 쓸 줄 안다 !

 

이렇게 컴포넌트를 만들어서 export해서 다른 컴포넌트에서도 쓸 수 있게 하는건가봐요.

 

좋은데? 재밌는데?

 

겉핥기만 해서 재밌는걸 수도 있지만, 일단 이 재밌는 상태를 유지하면서 계속 지선생님과 함께 배워보겠습니다.

 

이번 글은 여기까지 !

지인들과 함께 사이드 프로젝트를 하게 됐는데, 한 번도 해본적 없는 프론트를 맡게 됐습니다.

 

사실 전부터 토이프로젝트나 혼자서 프로젝트를 진행해보고 싶었는데, 백엔드는 할 수 있었지만 프론트를 아예 할 줄 몰라서 포기한 프로젝트들이 많았습니다.

 

그래서 이번 기회에! 혼자서 프론트까지 뚝딱뚝딱 할 수 있는 수준은 되어보고자 리액트 공부를 시작했습니다

 

강의나 책을 사볼까 고민도 했지만 주변 프론트엔드 개발자가 되고자 하는것은 아니기때문에, 그리고 리액트는 인기가 많은 만큼 레퍼런스도 많기 때문에 강의나 책은 안보기로 했습니다.

 

주변 프론트개발자들에게 깊게는 아니더라도 혼자 뚝딱뚝딱 할 수 있는 수준으로 하려면 어떻게 해야하나 물어보니,

 

"일단 프로젝트 하면서 머리 박으면서 해보는게 최고다"

 

라는 말을 많이 하길래, 일단 머리 박아보기전에? 기초라도 알기 위해 지피티를 이용해 간단하게 배워보려 합니다.

 

GPT에게 "리액트 배워보고싶고, 로드맵 알려줘" 라고 하니까

 

다음과 같이 답변했습니다.

 


React를 처음 배우고 사이드 프로젝트에 적용하는 것을 목표로 하는 로드맵을 아래에 제안해 드릴게요. 기초부터 실습을 통해 자연스럽게 익히는 과정을 제안하며, 실전에서 쓸 수 있는 스킬에 중점을 두겠습니다.

1. 기본 개념과 환경 설정

목표: React가 무엇인지 이해하고, 개발 환경을 설정하기.

  • React의 개념 이해하기: React의 철학, 컴포넌트 기반 구조, 선언형 UI가 무엇인지 알아보기.
  • 개발 환경 설정: Node.js와 npm 설치, Vite나 Create React App으로 기본 React 프로젝트 생성.
  • JSX: JSX 문법을 이해하고 HTML과의 차이점 학습.
  • React 컴포넌트 기본: 함수형 컴포넌트와 Props 개념 익히기.

2. React 기초 다지기

목표: React 컴포넌트를 구성하고 상태(state)와 효과(effect)를 이해하기.

  • State와 Props: State와 Props의 차이를 이해하고, Props를 통한 데이터 전달 학습.
  • useState: 상태 관리를 위한 useState 훅 학습.
  • 이벤트 처리: 버튼 클릭 등 기본 이벤트 처리 방식 익히기.
  • useEffect: 컴포넌트 라이프사이클 이해와 useEffect 훅 학습. 데이터를 비동기로 불러올 때 사용 방법 익히기.

3. React로 간단한 프로젝트 만들어보기

목표: React로 간단한 기능을 구현하며 개념을 익히기.

  • To-do List 만들기: 상태와 이벤트를 활용해 기본적인 CRUD 기능을 구현.
  • 간단한 API 호출: 공공 API를 활용해 데이터를 받아와서 렌더링하는 연습.

4. 스타일링과 UI 라이브러리

목표: 기본 스타일링 방법과 UI 라이브러리를 사용하여 프로젝트를 좀 더 보기 좋게 만들기.

  • CSS 기본 스타일링: CSS 파일을 활용해 컴포넌트 스타일링하기.
  • Styled-Components 또는 Tailwind CSS: JavaScript 기반 스타일링 라이브러리 사용해보기.
  • UI 라이브러리 사용: Material-UI, Ant Design, 또는 Bootstrap 등을 활용해 기본 UI 구성 익히기.

5. 라우팅과 상태 관리 심화 학습

목표: React에서 라우팅을 설정하고, 프로젝트 규모가 커질 때 필요한 상태 관리 기법을 익히기.

  • React Router: 여러 페이지 간의 이동을 위한 라우팅 설정.
  • Context API와 Redux: 글로벌 상태 관리 방법으로 Context API와 Redux 이해.

6. 프로젝트 실습과 고도화

목표: 사이드 프로젝트에서 필요한 기능을 구현하며 실전 경험 쌓기.

  • 프로젝트 설정: 전체 프로젝트의 구조를 설정하고, 필요한 페이지와 기능 나열.
  • Form 관리: 사용자 입력을 받는 폼 구성하기.
  • 실전 API 연동: 로그인, 회원가입, 데이터 조회 등 실제 API 연동 기능 추가.
  • 성능 최적화: React.memo, useMemo, useCallback과 같은 성능 최적화 방법 익히기.

이 로드맵을 통해 기초부터 실전 적용까지 차근차근 학습할 수 있도록 도움 드릴게요. 각 단계별로 궁금한 부분이나 어려운 점이 있다면 언제든지 질문해 주세요.


 

리액트에 대한 지식이 전무하기 때문에, 일단 지피티를 따라가보겠습니다.

 

실습내용과 코드는 다음 글부터 쓰겠습니다!

 

이번 글은 여기까지!

+ Recent posts