[프로그래머스] 프론트엔드 기초: React + TypeScript(9)

Lina Hongbi Ko·2024년 10월 31일
0

Programmers_BootCamp

목록 보기
46/76
post-thumbnail

2024년 10월 31일

✏️ 클래스형 컴포넌트 & 함수형 컴포넌트

  • 클래스형 컴포넌트

    • 리액트 초기 버전에서 주로 사용했음

    • 예전에 구현 했던 레거시 시스템에 많이 사용했기 때문에 (지금도 여전히 일부에서는 사용) 알고 있어야함

      // ClassCom.tsx
      
      // 클래스형 컴포넌트 만들어보기
      import { Componnet } from 'react'; 
      
      class ClassCom extends Component{
          render() {
              return(
                  <div>
                      클래스형 컴포넌트
                  </div>
              )
          }	
      }
      
      export default ClassCom;
  • 함수형 컴포넌트

    • 리액트 16버전 이후로 권장하는 컴포넌트 방식

    • state, lifecycle를 보완해서 최근에는 함수형 컴포넌트를 더 선호하고 잘 씀 → 가독성면에서 더 좋음

      // FuncCom.tsx
      
      import React from 'react';
      
      function FuncCom() {
        return(
          <div>
            함수형 컴포넌트
          </div>
        );
      }
      
      export default FuncCom;
  • 함수형 컴포넌트는 sate와 lifecycle의 불편함이 있었는데, hook이라는 개념을 도입해 해결했음 → 현재 함수형 컴포넌트를 권장하고 있음

  • 모던자바스크립트에서 화살표 함수를 이용해 함수형 컴포넌트를 만들 수도 있음

    // FuncCom.tsx
    
    import React from 'react';
    
    const FuncCom = () => {
      return(
        <div>
          함수형 컴포넌트
        </div>
      );
    }
    
    export default FuncCom;

✏️ state 사용하기

// Todolist.tsx

import React from 'react';

const TodoList : React.FC = () => { // 리액트 컴포넌트의 타입 정의
									// props의 타입을 명시하기 위해 사용
	// const title = "오늘 할 일";
	// title = 100;
	// 문자열로 타입이 결정되었는데 숫자를 재할당하면 에러가 난다
	// 따라서 타입을 적어줘야함
	const title : string = "오늘 할 일"
	
    // state 데이터의 상태를 나타냄
    // 데이터가 변경되면 즉시 화면에 반영
    // 일반 변수를 사용하면 상태 관리가 안되지만, state를 사용해 상태 관리할 수 이음
    // useState() 훅을 사용해 state를 사용할 수 있음
    // 그리고 todos가 여러 개이면 state를 하나씩 넣어주는 것은 불가능
    // 따라서, 배열 사용해서 넣어준다 -> 배열을 사용할때 인덱스를 가져와서 나열해줌(대부분의 프로그래밍이 그럼)
  const [todos] = useState(['공부하기', '잠자기', '놀기']);
  
  return(
	  <div>
		  <h1>{title}</h1>
		  <p></p>
		  <div className='container'>
            <ul>
              <li>{todos[0]}</li>
              <li>{todos[1]}</li>
              <li>{todos[2]}</li>
            </ul>
    	</div>
	  </div>
  );
}

export default TodoList;

✏️ 구조 분해 할당

  • 다량의 변수를 배열의 형태로 관리 할 수 있는 것을 알게 되었다.

  • 그런데 state에 직접 접근해서 접근하는 것은 불가능하다 → 별도의 변경함수를 이용해서 바꿔야함

    • useState 선언시 두번째 요소에 변경 함수를 선언해준다.
      • const [todos, setTodos] = useState([’공부하기’ , ‘잠자기’, ‘놀기’]);
      • 첫번째 요소는 state에 데이터가 들어가고, 두번째 요소는 변경 함수임
  • 클래스 구성요소에는 멤버 변수, 멤버 함수가 있었다. 클래스의 캡슐화 철학을 기반으로 멤버 변수를 보호하고 멤버 함수로 멤버 변수를 접근하도록 만들었다. 이와 비슷하게 state도 외부에서 마음대로 바꾸지 못하고, setTodos(함수) 를 사용해 변경 할 수 있는 것과 같은 이치

    // Todolist.tsx
    
    import React, { useState } from 'react';
    
    const TodoList : React.FC = () => {
      const title : string = "오늘 할 일";
      const [todos, setTodos] = useState<string[]>(['공부하기', '잠자기', '놀기']); // 각 데이터가 문자열이므로 타입을 추가함
      // 원래는 우항의 데이터 전체(메모리 주소값)를 좌항에 주소값을 넘겨주는 형태로 사용 했는데,
      // 이제 구조 분해 할당을 사용하면 그 안의 데이터들을 뽑아내서 바로 좌항에서 사용할 수 있음
        /*
        const person = {
            name : 'lee',
            age: 20,
            cit : 'seoul'
        }
        const {name, age, city} = person;
        console.log(name); // lee
        console.log(age); // 20
        console.log(city); // seoul
        */
      return(
        <div>
          <h1>{title}</h1>
          <p></p>
          <div className='container'>
            <div className='board'>
              <ul>
                <li>{todos[0]}</li>
                <li>{todos[1]}</li>
                <li>{todos[2]}</li>
              </ul>
            </div>
          </div>
        </div>
      );
    }
    
    export default TodoList;

✏️ 데이터 반복 처리

  • map을 통해 반복되는 데이터 보여주기

    // MapTeste.tsx
    
    import React, {} from 'react';
    
    const MapTest = () => {
      const fruits = ['apple', 'banana', 'orange'];
    
      return(
        <div>
          <h2>과일</h2>
          <ul>
            {
              fruits.map((fruit, idx)=>(<li key={idx}>{fruit}</li>))
            }
          </ul>
        </div>
      )
    }
    
    export default MapTest;
  • 내가 타입을 만들어서 사용하기

    // Todolist.tsx
    
    import React, { useState } from 'react';
    
    type Todo = {
        id : number;
        text : string;
        isChecked : boolean;
    };
    // 인터페이스가 아닌 type을 사용한 이유
    // 인터페이스는 주로 객체의 구조를 정의할때 사용
    // type을 새로 지정하기 위해서는 type이 더 적합
    // 그리고 데이터를 입력할 때 type 형식에 맞게 들어가야함
    
    const TodoList : React.FC = () => {
      const title : string = "오늘 할 일";
      const [todos, setTodos] = useState<Todo[]>([
        {id: 1, text: '공부하기', isChecked: false},
        {id: 2, text: '잠자기', isChecked: false},
        {id: 3, text: '놀기', isChecked: false}
      ]);
      return(
        <div>
          <h1>{title}</h1>
          <p></p>
          <div className='container'>
            <div className='board'>
              <ul>
                {
                    todos.map((todo, idx)=>(<li key={idx}>{todo.text}</li>))
                }
              </ul>
            </div>
          </div>
        </div>
      );
    }
    
    export default TodoList;

✏️ 체크박스 기능

// Todolist.tsx

import React, { useState } from 'react';

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

const TodoList : React.FC = () => {
  const title : string = "오늘 할 일";

  // 구조분해할당
  const [todos, setTodos] = useState<Todo[]>([
    {id: 1, text: '공부하기', isChecked: false},
    {id: 2, text: '잠자기', isChecked: false},
    {id: 3, text: '놀기', isChecked: false}
  ]);

  const handleCheckedChange = (itemId : number) => {
    setTodos((prevItems) => 
      prevItems.map((item) => item.id === itemId ? {...item, isChecked : !item.isChecked} : item)
    );
  }
  return(
    <div>
      <h1>{title}</h1>
      <p></p>
      <div className='container'>
        <div className='board'>
          <ul>
            {
              todos.map((todo, idx) => (
              <li key={todo.id}>
                <input type="checkbox" onChange={()=>{
                  handleCheckedChange(todo.id)
                }}></input>
                <span>
                  {
                    todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
                  }
                </span>
              </li>))
            }
          </ul>
        </div>
      </div>
    </div>
  );
}

export default TodoList;

✏️ 게시물 추가

  • 부트스트랩 사용하기

    • React Bootstrap
      • npm install react-bootstrap bootstrap
  • 코드 작성

    // Todolist.tsx
    
    import React, { useState } from 'react';
    import { Button } from 'react-bootstrap';
    
    type Todo = {
      id : number;
      text : string;
      isChecked : boolean;
    }
    
    const TodoList : React.FC = () => {
      const title : string = "오늘 할 일";
    
      // todos
      const [todos, setTodos] = useState<Todo[]>([
        {id: 1, text: '공부하기', isChecked: false},
        {id: 2, text: '잠자기', isChecked: false},
        {id: 3, text: '놀기', isChecked: false}
      ]);
    
      // newtodo
      const [newTodo, setNewTodo] = useState<string>('');
    
      const handleCheckedChange = (itemId : number) => {
        setTodos((prevItems) => 
          prevItems.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('');
        }
      }
      return(
        <div>
          <h1>{title}</h1>
          <p></p>
          <div className='container'>
            <div>
              <input type="text" placeholder='할 일 입력' style={{marginRight: '10px', writingMode : 'horizontal-tb'}} onChange={(e) => setNewTodo(e.target.value)}/>
              <Button onClick={addTodo}>추가</Button>
            </div>
            <p></p>
            <div className='board'>
              <ul>
                {
                  todos.map((todo, idx) => (
                  <li key={todo.id}>
                    <input type="checkbox" onChange={()=>{
                      handleCheckedChange(todo.id)
                    }}></input>
                    <span>
                      {
                        todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
                      }
                    </span>
                  </li>))
                }
              </ul>
            </div>
          </div>
        </div>
      );
    }
    
    export default TodoList;
  • 스프레드 연산자 : 기존의 배열을 변경하지 않고, 새로운 요소를 이용해서 변경할 때 사용

    • 원본은 유지하되, 새로운 것을 적용시켜줌

    • 똑같은 내용이지만 주소값 다름

      let a = [1, 2, 3];
      let b = a;
      console.log(a === b); // true -> 얕은 복사
      
      let c = [...a];
      console.log(a === c); // false -> 깊은 복사

✏️ 타이머, 시계

  • 타이머 컴포넌트 만들기

    • setInterval(콜백함수, 시간주기) 이용
    // Timer.tsx
    
    import React, { useState } from 'react';
    
    const Timer : React.FC = () => {
      const [seconds, setSeconds] = useState<number>(0);
    
      return(
        <div>
          <h2>타이머 : {seconds}초</h2>
          <button onClick={()=>{
            setInterval(()=>{
              setSeconds(prev => prev + 1);
            }, 1000) // 1초마다 타이머 동작
          }}>시작</button>
        </div>
      );
    }
    
    export default Timer;
  • 시계 컴포넌트 만들기

    // Clock.tsx
    
    import React, { useState } from 'react';
    
    const Clock : React.FC = () => {
      const [time, setTime] = useState(new Date());
    
      setInterval(()=>{
        setTime(new Date());
      }, 1000);
    
      return(
        <div>
          현재 시간 : {time.toLocaleTimeString()}
        </div>
      );
    }
    
    export default Clock;

✏️ 게시물 삭제

// Todolist.tsx

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

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

const TodoList : React.FC = () => {
  const title : string = "오늘 할 일";

  // todos
  const [todos, setTodos] = useState<Todo[]>([
    {id: 1, text: '공부하기', isChecked: false},
    {id: 2, text: '잠자기', isChecked: false},
    {id: 3, text: '놀기', isChecked: false}
  ]);

  // newtodo
  const [newTodo, setNewTodo] = useState<string>('');

  const handleCheckedChange = (itemId : number) => {
    setTodos((prevItems) => 
      prevItems.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 deleteTodo = (id : number) => {
    setTodos(todos.filter((todo)=> todo.id !== id));
  }
  return(
    <div>
      <h1>{title}</h1>
      <p></p>
      <div className='container'>
        <div>
          <input type="text" placeholder='할 일 입력' style={{marginRight: '10px', writingMode : 'horizontal-tb'}} onChange={(e) => setNewTodo(e.target.value)}/>
          <Button onClick={addTodo}>추가</Button>
        </div>
        <p></p>
        <div className='board'>
          <ul>
            {
              todos.map((todo, idx) => (
              <li key={todo.id}>
                <input type="checkbox" onChange={()=>{
                  handleCheckedChange(todo.id)
                }}></input>
                <span>
                  {
                    todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
                  }
                </span>
                <button onClick={() => deleteTodo(todo.id)} className='delbutton'>삭제</button>
              </li>))
            }
          </ul>
        </div>
      </div>
    </div>
  );
}

export default TodoList;
profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글