항해99의 4주차 돌아보기

Edwin·2023년 3월 9일
  • 본 포스트는 항해99의 과정 가운데 4주차 내용에 대한 리뷰입니다.

리액트와 친해지기 + Redux

먼저, vscord를 통해 종료하지 않은 포트 종료하기

create react-app을 통해서 새로운 리액트 프로젝트를 신설하고, yarn start를 통해서 프로젝트를 실행하면 localhost:3000 에서 프로젝트가 실행된다. 그런데 문제는 프로젝트를 종료하지 않으면 포트가 종료되지 않는 문제가 발생되며, 계속 새로운 포트를 열어줘야 한다는 문제가 발생되었다. 그래서 실행 중인 포트를 kill 해 줄 필요가 있었다.

  • sudo lsof -PiTCP -sTCP:LISTEN
    현재 사용 내 맥북에서 사용중인 포트를 확인해보자.
  • sudo lsof -i :3000
    물론 특정포트만 검색하는 것도 가능하다.
  • 예시 : sudo kill -9 PID
    사용하기 : sudo kill -9 3000
    검색하면, 사용중인 포트의 PID 번호를 알 수 있고, 해당 PID를 입력하면, 포트를 종료할 수 있다.

1) 중앙 상태관리 생성하기

리덕스에서 리액트를 실행한다는 부분은 전역에서 사용할 상태관리소를 만들기 위해서이다. 전역관리소 자체는 useContext() 훅을 통해서 제어할 수도 있지만, Redux는 useContext()에 비해서 크고 방대한 시스템을 구축할 수 있다.

ChapGPT : useContext와 Redux

  • useContext와 Redux는 둘 다 React 애플리케이션에서 상태 관리를 위해 사용되는 방법입니다.
  • useContext는 React Hooks API의 일부로, 컴포넌트 간에 상태를 공유하기 위한 간단한 방법을 제공합니다. useContext는 Context API를 사용하여 부모 컴포넌트에서 하위 컴포넌트로 상태를 전달할 수 있습니다. 예를 들어, 사용자 인증 상태와 같은 전역 상태를 다른 컴포넌트에서 사용해야 할 때, useContext를 사용하여 이러한 데이터를 더 쉽게 전달할 수 있습니다.
  • Redux는 컴포넌트 간에 전역 상태를 관리하는 방법을 제공합니다. Redux는 Redux 스토어라고 하는 중앙 집중식 데이터 저장소를 사용하여 애플리케이션의 상태를 관리합니다. Redux는 상태를 변경하는 데 사용되는 액션과 액션을 처리하는 리듀서를 사용합니다. 이를 통해 여러 컴포넌트가 동일한 상태를 공유할 수 있으며, 액션 및 리듀서의 사용으로 상태 변경의 추적 및 디버깅이 쉬워집니다. 즉, useContext는 단순한 컴포넌트 간 상태 공유에 사용되는 반면, Redux는 전역 상태 관리를 위한 더 복잡한 방법을 제공합니다. 또한 Redux는 추가적인 라이브러리 및 패턴을 사용하여 더 많은 기능을 제공하며, 대규모 애플리케이션에서 확장성을 제공합니다. 반면, useContext는 React Hooks API의 일부이므로 React와 함께 더 간편하게 사용할 수 있습니다.

바닐라React에서 useContext()를 통해서 상태를 공유할 수 있지만, 상태를 변경하기 위해서는 useReducer()를 함께 사용해야되는 반면에, Redux는 자체적으로 위에서 언급한 두 개의 react-HOOKS를 지원한다. 물론 세부적으로 보면 복잡하다. config폴더(configStore)와 modules폴더(reducer함수)를 만들어줘야 하기 때문이다.

2) Modules에서 리듀서 관리하기

const todos = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state
  }

위의 코드가 가장 기본적으로 리듀서를 통해서 관리될 상태를 생성하는 방법이다. 그리고 위에서 초기값으로 선언한 내용은 아래와 같이 선언할 수 있다.

const initialState = {
  // 객체 안에 2개의 프로퍼티가 존재한다.  
  todos: [
    {
      id: "1",
      title: "리액트",
      body: "리액트를 배워봅시다",
      isDone: false,
    },
  ],
  todo: {
    id: "0",
    title: "",
    body: "",
    isDone: false,
  },
};
  • todos 프로퍼티 : 전역에서 사용할 상태
  • todo 프로퍼티 : reducer 함수에서 value 하나를 가공 입력/전달 할 때

상태변경(1) CRUD : 생성하기

중복되지 않은 value 생성하기

이번 프로젝트에서는 map 매서드를 통해서, 상태의 값 각각을 화면에 뿌리는 프로젝트를 진행하였고, 해당 부분의 중복되지 않은 key 속성을 부여하였다. 이는 추후에 CRUD를 위해서 이다. 만약 고유한 key가 존재하지 않으면, 콘솔에서 오류메시지를 보여준다.

  • Warning: Encountered two children with the same key, 0. Keys should be unique...

노마드코더의 니코쌤

Date.now()

Date 객체를 활용하면, 1970년 1월 1일 0시 0분 이후 현재의 밀리세컨드 단위의 차이에 해당되는 숫자열을 반환받을 수 있다.

nextId() - react-id-generator

터미널 : yarn add react-id-generator

오늘의 예시에서 소개된 것은 nextId()였다. npmjs의 설명에 따르면, 고유한 아이드를 반환해주는 간단한 함수라고 소개한다. 해당 함수가 선언될 때마다 id값을 순차적으로 증가시키며 반환해준다.

함수가 실행되는 것은 화면이 '리렌더링' 되었을 때이다. 그렇기에 onChange()를 통해서 input에 입력값이 1bit만 추가되어도 값이 변경되어, 실제로 id가 상태에 저장될 때 꼭 id1, id2 순서대로 기록되지는 않는다. 입력이 끝난시점의 리렌더링이 끝난 시점에서 동작하게 된다.

useId() - react

또다른 방법은 react의 내장HOOKS인 useId()를 활용하는 방법이다. 그런데 값이 증가되지 않는다? 이 문제는 나중에 해결해보자.

상태변경(2) CRUD : 읽기

개발자들이란 반복을 극단적으로 싫어하는 직군이다. 이는 비용과 관련이 있기 때문일 것이다. 그러기에 만약 상태 안에 블리언자료형이 존재한다면, 리듀서함수를 줄일 수도 있고 뷰(return 아래)부분의 코드도 별도로 작성할 필요가 없다.

  • 예시를 보자.
const initialState = {
  todos: [
    {
      id: "1",
      title: "리액트",
      body: "리액트를 배워봅시다",
      isDone: false,
    },
...    

initialState를 기록한 것까지는 동일했다. 그러나 이를 활용하는 부분에서는 달랐다. 결과를 보면 나는 커스텀훅을 만들어서 제어했기 때문에 컴포넌트 선언과 반환문 사이가 줄었다는 차이뿐이다.

나의 기록을 살펴보자.

// TDState 컴포넌트를 만들고 제어를 했다면, 
function TDArticle() {
  return (
    <TDArticle_Layout>
      <TDState isDone={false} />
      <TDState isDone />
    </TDArticle_Layout>
  )
}

function TDState({isDone}) {
  const [todo,dispatch] = useConfigTodo();

  return (
    <div>
      {!isDone ? <p><span>할일목록</span></p> : <p><span>완료목록</span></p>}
      {todo.map(el =>
        el.isDone === isDone && (
          <div key={el.id}>
            <Ptag>{el.title}</Ptag>
            <TDEdite item={el} />
            <button className='btngap' onClick={()=>{dispatch(doneTodo(el.id))}}>완료</button>
            <button onClick={()=>{dispatch(deleteTodo(el.id))}}>삭제</button>
          </div>
        ))}
    </div>
  )
}

정답예시는 TDState 컴포넌트 내에서 isDone의 상태에 따라서 기록해주었다. 그렇게 함으로 중계만 해주는 TDArticle_Layout 컴포넌트를 만들어주지 않아도 되었다.

// Home 컴포넌트를 만들고, 그 안에 List 컴포넌트를 만들어서 들어갔다. 
const Home = () => {
  return (
    <Layout>
      <Header />
      <Form />
      <List />
    </Layout>
  );
};

// List 
const List = () => {
    return (
    <StListContainer>
    // StListContainer 태그 시작
      
      // 01 Working.. 🔥 if (!todo.isDone)
      <h2>Working.. 🔥</h2>
      <StListWrapper>
      {todos.map((todo) => {
      if (!todo.isDone) {
        return (
         	<StTodoContainer key={todo.id}>...
         		 <StButton>{todo.isDone ? "취소!" : "완료!"}</StButton>)
  		    </StTodoContainer>
       </StListWrapper>
       // StListWrapper 끝

      // 02 Done..! 🎉 if (todo.isDone)  
      <h2 className="list-title">Done..! 🎉</h2>  
      <StListWrapper>
      {todos.map((todo) => {
      if (todo.isDone) {
        return (
         	<StTodoContainer key={todo.id}>...
         		 <StButton>{todo.isDone ? "취소!" : "완료!"}</StButton>)
  		    </StTodoContainer>
       </StListWrapper> 
       // StListWrapper 끝
        
    // StListContainer 태그 끝        
    <StListContainer>

03 react-router-dom : 상세페이지

상세페이지에서 중앙상태관리소에서 reducer함수를 불러와, useParams를 통해서 url의 값을 받아오고, 이를 비교한 프로퍼티를 가져와서 뷰에 표현했다.

function TDDetail() {
  const [todo] = useConfigTodo();
  const prams = useParams();
  const findtodo = todo.find(el => el.id === Number(prams.id))

예시코드 살펴보기

useEffect(()=> {
    dispatch(getTodoByID(id))
  }, [dispatch, id])

useEffect를 사용하여 컴포넌트가 마운트(mount) 되었을 때, dispatch(getTodoByID(id) 리듀서 함수가 실행되도록 설정했다. getTodoByID()를 살펴보면 아래와 같다. 모듈로 이동하자.

 case GET_TODO_BY_ID:
      return {
        ...state,
        todo: state.todos.find((todo) => {
          return todo.id === action.payload;
        }),
      };

나의 경우에는 상세페이지에서 이를 처리했다면, 예시코드는 이를 dispatch를 통해서 action으로 제어했다는 것이다. 그리고 이 부분에서 초기값에서 중앙 상태가 아니라, 가공을 위해 만들어놓은 객체인 todo를 이용하여 관리했다는 점이다.

04 정리하면

코드의 세계는 방대하고 접근하는 부분에 따라서 상의하다는 점이다. 어떻게 이를 활용할지에 대해서는 개인의 선택이다. 그러나 나중에 회사에 가게 된다면, 회사가 결정의 주체가 될 것이다. 그러기에 다양한 방법에 대해서 알고 있다면, 쉽게 나를 맞출 수 있을 것이다.

뿐만 아니라 배우는 것은 코드의 한 줄 길이를 속성값에 따라서 줄바꿈을 해준다는 것이지 않을까?

author. EDWIN
date. 23/03/09

profile
신학전공자의 개발자 도전기!!

0개의 댓글