[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 23일차

🍊 React

1. React 핵심개념

컴포넌트

  1. 기존 MVC 패턴은 각 요소간 의존성이 높아 재사용이 어려움
  2. React 컴포넌트는 view를 독립적으로 구성, 재활용이 용이함

컴포넌트 구성 요소

데이터 구성 요소 특징
State 컴포넌트의 상태를 저장하고 변경 할 수 있는 데이터
Props 부모 컴포넌트에서 자식 컴포넌트로 전달되는 읽기 전용 데이터
Context 부모 컴포넌트에서 생성하여 모든 자식 컴포넌트에 전달하는 데이터

컴포넌트 종류

컴포넌트 종류 특징
함수형 컴포넌트 JavaScript에서 사용하는 함수를 이용해 만든 컴포넌트
클래스형 컴포넌트 ES6에서 지원하는 class 문법을 이용해 만든 컴포넌트

✅ 함수형 컴포넌트를 사용하도록 권장

  • 함수형 컴포넌트가 가독성이 좋고 간결
  • Hook의 등장으로 사용하기 편함
  • render 함수가 필요 없어 마운트 속도가 빠름
// 함수형 컴포넌트 생성
function Component(props){
  return <div>Hello</div>
}

function App(){
  return(
    <Component /> // 호출
  )
}

export default App; // 다른 js파일에서 호출 할 수 있도록 내보내기

Hook

함수형 컴포넌트가 클래스형 컴포넌트 기능을 해줄 수 있게 하는 함수

State Hook 👇🏻

  • useState: 동적으로 바뀌는 값 관리 (컴포넌트 내부)
  • useReducer: 컴포넌트 상태 업데이트 로직을 분리하여 관리

Effect Hook 👇🏻

  • useEffect: 컴포넌트가 렌더링 될 때마다 특정작업을 수행할 수 있게 하는 함수

State (useState)

  • 동적으로 바뀌는 값을 관리할 때 사용, 상태 유지 값과 그 값을 갱신하는 함수를 반환
  • 함수 컴포넌트 내부에서 상태 업데이트 관리

const[상태 값 저장 변수, 상태 값 갱신 함수] = useState(상태 초기값)

State (useReducer)

  • 함수 컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리하여 사용
  • 현재 상태 객체행동 객체를 인자로 받아, 새로운 상태 객체를 반환하는 Hook 함수

const[상태 객체, dispatch 함수] = useReducer(reducer함수, 초기 상태, 초기 함수)

함수명 함수 정의 설명
reducer 함수 reducer(state, action) 현재 상태객체(state)와 행동 객체(action)를 인자로 받아서 새로운 state를 반환
dispatch 함수   생태 변경을 일으키기 위해 사용되는데 reducer 함수에 넘길 action 객체를 받음

Props

  • 컴포넌트에 값을 전달하기 위해 사용
  • 상위 컴포넌트에서 하위 컴포넌트로 전달되는 읽기 전용 데이터
  • 객체 형태로 전달되며 props 이름은 동일해야함
function Hello(props){
  return (
    <div>
      안녕하세요,
      저는 {props.name}입니다.
      좋아하는 색상은 {props.color}입니다.
    </div>
  )
}

Effect (useEffect)

  • 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 실행할 수 있게 하는 hook

useEffect(실행할 함수, 의존성 배열)

의존성 배열이 없을 경우 👇🏻

React.useEffect(() => {
  dispatch(getActions.getFundingAC(page));
});
  • 페이지가 렌더링 될 때마다 함수(dispatch)가 실행

의존성 배열이 비어있을 경우 👇🏻

React.useEffect(() => {
  dispatch(getActions.getFundingAC(page));
}, []);
  • 첫 렌더링 시에만 함수(dispatch)가 실행

의존성 배열에 값이 있을 경우 👇🏻

const [page, setPage] = React.useState(1)

React.useEffect(() => {
  dispatch(getActions.getFundingAC(page));
}, [page]);
  • 해당값(page) 값이 변할 때마다 함수(dispatch)가 실행

2. 과제 - Momentum(Ract)

App.js

import './App.css';
import React, { useState, useEffect } from 'react';

const WeatherIcons = {
  '01': 'fas fa-sun',
  '02': 'fas fa-cloud-sun',
  '03': 'fas fa-cloud',
  '04': 'fas fa-cloud-meatball',
  '09': 'fas fa-cloud-sun-rain',
  '10': 'fas fa-cloud-showers-heavy',
  '11': 'fas fa-poo-storm',
  '13': 'far fa-snowflake',
  '50': 'fas fa-smog',
};

function App() {
  const [weatherIcon, setWeatherIcon] = useState('');
  const [temperature, setTemperature] = useState('');
  const [city, setCity] = useState('');
  
  useEffect(() => {
    const API_KEY = 'd126eafe75d75dc8f3cb05542db8df4d';

    const handleGeoSuccess = (pos) => {
      const { latitude, longitude } = pos.coords;

      fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
      )
        .then((response) => response.json())
        .then((data) => {
          const iconCode = data.weather[0].icon.substr(0, 2);
          const temperature = Math.floor(data.main.temp) + '';
          const city = data.name;

          setWeatherIcon(iconCode);
          setTemperature(temperature);
          setCity(city);
        })
        .catch((error) => {
          console.log('Error fetching weather data:', error);
        });
    };

    const handleGeoError = () => {
      console.log('Error getting geolocation.');
    };

    navigator.geolocation.getCurrentPosition(handleGeoSuccess, handleGeoError);
  }, []);

  const [time, setTime] = useState('');
  
  useEffect(() => {
    const interval = setInterval(() => {
      const date = new Date();
      const hours = String(date.getHours()).padStart(2, '0');
      const minutes = String(date.getMinutes()).padStart(2, '0');
      const seconds = String(date.getSeconds()).padStart(2, '0');

      setTime(`${hours}:${minutes}:${seconds}`);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  const [username, setUsername] = useState('');
  const [greeting, setGreeting] = useState('');

  useEffect(() => {
    const savedUsername = localStorage.getItem('username');

    if (savedUsername) {
      setUsername(savedUsername);
    }
  }, []);

  useEffect(() => {
    const date = new Date();
    const time = date.getHours();
    let greetingWord = '';

    if (0 <= time && time < 5) {
      greetingWord = 'Good day';
    } else if (5 <= time && time < 9) {
      greetingWord = 'Good morning';
    } else if (9 <= time && time < 17) {
      greetingWord = 'Good afternoon';
    } else {
      greetingWord = 'Good evening';
    }

    setGreeting(`${greetingWord} ${username} :D`);
  }, [username]);

  const handleLoginSubmit = (e) => {
    e.preventDefault();
    const enteredUsername = e.target.elements.username.value.trim();

    if (enteredUsername) {
      setUsername(enteredUsername);
      localStorage.setItem('username', enteredUsername);
    }
  };

  const [todos, setTodos] = useState(() => {
    const savedTodos = localStorage.getItem('todos');

    if (savedTodos) {
      try {
        return JSON.parse(savedTodos);
      } catch (error) {
        console.log('Error parsing todos data:', error);
      }
    }

    return [];
  });

  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]);


  const deleteTodo = (id) => {
    setTodos((prevTodos) => {
      const updatedTodos = prevTodos.filter((todo) => todo.id !== id);
      localStorage.setItem('todos', JSON.stringify(updatedTodos)); // 변경된 todos 값을 로컬 스토리지에 저장
      return updatedTodos;
    });
  };

  const handleTodoSubmit = (e) => {
    e.preventDefault();
    const newTodo = e.target.elements.todo.value.trim();

    if (newTodo) {
      const todoItem = {
        text: newTodo,
        id: Date.now(),
      };

      setTodos((prevTodos) => {
        const updatedTodos = [...prevTodos, todoItem];
        localStorage.setItem('todos', JSON.stringify(updatedTodos)); // 변경된 todos 값을 로컬 스토리지에 저장
        return updatedTodos;
      });
      e.target.reset();
    }
  };

  return (
    <>
      <header>
        <div id="weather">
          <span className="CurrIcon">
            <i className={weatherIcon ? WeatherIcons[weatherIcon] : ''}></i>
          </span>
          <span className="CurrTemp"> {temperature} </span>
          <span className="City">{city}</span>
        </div>
      </header>
      <main>
        <div className="inner">
          <div className="clock">
            <h1 id="time">{time}</h1>
          </div>

          <div className="greeting">
            {!username ? (
              <form id="login_form" onSubmit={handleLoginSubmit}>
                <input
                  required
                  maxLength="15"
                  type="text"
                  name="username"
                  placeholder="What is your name?"
                />
                <button>Log in</button>
              </form>
            ) : (
              <h2 id="greetings">{greeting}</h2>
            )}
          </div>

          <div className="todo">
            <form id="todo_form" onSubmit={handleTodoSubmit}>
              <input required type="text" name="todo" placeholder="Write a To Do and Press Enter." />
            </form>

            <ul id="todo_list">
              {todos.map((todo) => (
                <li key={todo.id}>
                  <span>{todo.text}</span>
                  <button onClick={() => deleteTodo(todo.id)}></button>
                </li>
              ))}
            </ul>
          </div>
        </div>
      </main>
    </>
  );
}

export default App;

3. 결과

Main

react1

인사말

react2

할 일 목록

react3

4. 회고

React에 대해 배우면서 나도 아직 헷갈리는게 많다고 생각이 들었다. 바로바로 나와야하는데 안나오는 기분이랄까?
그래도 막 엄청나게 어렵다거나 그렇지는 않은거같다. 물론 css가 제일 귀찮ㅇ..ㅏ 이번에 Figma를 vscode에서 플러그인으로 사용할 수 있다길래 한번 써봤다.
Figma에서 설정되어있는 css를 바로 넣을 수 있다. 신기함 !! 혹시라도 제 과제의 회고를 보신다면 한번 해보시길.. 이번년도 까지만 무료라고 써져있음!
이정도면 퍼블리셔 없어지는거 아니냐구요ㅠㅠㅠ 그만 발전해.. 암튼 원래 컴포넌트를 나눠서 하려고했는데 하다보니 App.js에 다 때려넣어 버렸다😆
저번주에 과제가 있는 줄 모르고 안해버려서 저번주꺼 까지 같이 하느라 시간이 좀 오래 걸렸다. 슬랙에 매번 올려주셨었는데, 이번엔 안올라와서 없는 줄 알았다. 앞으로 노션을 잘 확이해봐야겠다ㅠ
내일은 프로젝트 설명회가 있는 날이라 기대된다. 재밌는 프로젝트를 선점해서 할 수 있는 기회가 왔으면 좋겠다! 앞으로도 화이팅!



본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.

#프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프

Leave a comment