2023.04.26 TIL

0

TIL

목록 보기
18/37
post-thumbnail

오늘의 나는 무엇을 잘했을까?

리액트의 내부 원리에 대해 관심을 가지고 강의 외적인 부분 또한 공식문서를 찾아보며 개념 공부를 능동적으로 했다.

오늘의 나는 무엇을 배웠을까?

리액트로 데이터 다루기(실전 패턴 모음)

  • 검색 기능 구현 패턴
    • 보통은 이런 api를 사용하여 검색 get요청을 날린다.
    • fetch('[https://learn.codeit.kr/api/foods?limit=2&search=토마토](https://learn.codeit.kr/api/foods?limit=2&search=%ED%86%A0%EB%A7%88%ED%86%A0)');
    1. 검색어 받기

      1. 검색어를 입력받을 state를 만든다
      2. 입력받은 인풋 태그도 만든다
      3. 입력 폼을 만든다
    2. 리퀘스트 보내기

      1. useEffect의 디펜던시 리스트를 활용한다. 검색 버튼을 누르면 디펜던시에 있는 상태가 바뀌면서 리퀘스트를 재요청한다.
    3. 리스폰스 적용하기
      1. 검색어가 바뀌면 데이터를 통째로 바꿔준다(기존에 받은 데이터에 더해주는 것이 아닌)

      const [search, setSearch] = useState('');
      
      const handleSearchSubmit = (e) => {
        e.preventDefault();
        setSearch(e.target['search'].value);
      };
      
      <form onSubmit={handleSearchSubmit}>
        <input name="search" />
        <button type="submit">검색</button>
      </form>

리액트에서 입력 폼 다루기

  • 기본 흐름
    1. 인풋값을 스테이트로 만든다
    2. 인풋 태그에 value로 스테이트를 설정한다
    3. 스테이트를 변경하는 함수를 인풋태그의 onChange이벤트에 등록한다
    4. onChange핸들러에서 setState(e.target.value);등으로 스테이트와 입력값의 싱크를 맞춰준다
  • form태그에서 onSubmit 이벤트핸들러 등록
    • form 태그의 디폴트 동작이 get리퀘스트를 보내는 것.
    • 이것을 막으려면 e.preventDefault()써야함.
  • 하나의 state로 폼 구현하기 - input태그의 name속성을 활용
     const [values, setValues] = useState({
     	title: "",
     	rating: 0,
     	content: "",
     });
     
     const handleChange = (e) =>{
     	const {name, value} = e.target;
     	setValues((prev)=>({
     		...prev,
     		[name]: value,
     	}));	
     }
     <form onSubmit={handelSubmit}>
     	<input name="title" value={values.title} onChange={handleChange}/>}
     </form>
  • 제어 컴포넌트
    • 인풋의 value를 리액트에서 지정해주는 컴포넌트
       const [value, setValue] = useState('');
       const handleChange = (e) =>{
       	const nextValue = e.target.value.toUpperCase();
       	setValue(nextValue);
       }
       
       return <input value={value} onChange={handleChange} />
  • 비제어 컴포넌트
    • 실제 인풋 value를 리액트에서 지정하지 않는 컴포넌트

  • 파일 인풋 받기
    • 입력된 파일들은 (사용자가 업로드한) e.target.files로 볼 수 있다. FileList객체이다.
    • input태그에 타입을 file로 해주면 된다.
    • 파일인풋은 value값을 지정할 수 없어 비제어 컴포넌트이다.
       function FileInput(){
       	const [value, setValue] = useState();
       	const handleChange = (e) =>{
       		console.log(e.target.files);
       	}
       
       	return <input type="file" />
       }
       
       ...
       function ReviewForm(){
       	..
       	return (
       		...
       		<FileInput />
       		...
       	);
       }
       
  • ref
    • ref를 쓰면 실제 DOM노드를 참조할 수 있다.
    • 주의할 점은 DOM노드는 항상 로딩이 끝나야 존재하기 때문에 ref객체의 current값 또한 로딩이 끝난 후에야 존재한다는 것을 알아야 한다.
    • 따라서 항상 다음과 같이 존재 여부를 체크하는 것을 권장한다
       const inputRef = useRef();
       
       useEffect(()=>{
       	if(inputRef.current){
       		console.log(inputRef.current);
       	}
       }, [])
       
       return <input type="text" ref={inputRef} />
  • ref를 이용한 파일 인풋 초기화
    • 파일 인풋은 value를 지정할 수 없어서 value를 접근하려면 FileInput컴포넌트에서 부모 컴포넌트로 직접 전달해주고, 값을 복사한 스테이트를 부모 컴포넌트에서 사용해야 한다.
    • 이번에는 ref객체를 이용해서 파일 인풋의 value를 null로 만들어보자. ref객체는 실제 DOM을 접근할 수 있어, value값을 바꿀 수 있다.
       import { useRef } from 'react';
       
       function FileInput({ name, value, onChange }) {
         const inputRef = useRef();
       
         const handleChange = (e) => {
           const nextValue = e.target.files[0];
           onChange(name, nextValue);
         };
       
         const handleClearClick = () => {
           const inputNode = inputRef.current;
           if (!inputNode) return;
       
           inputNode.value = '';
           onChange(name, null);
         };
       
         return (
           <div>
             <input type="file" onChange={handleChange} ref={inputRef} />
             <button type="button" onClick={handleClearClick}>
               X
             </button>
           </div>
         );
       }
       
       export default FileInput;
  • 이미지 미리보기(Object URL)
    • 사용자의 컴퓨터에 있는 파일을 ObjectURL을통해 넷 상에서 주소로 활용할 수 있다.
       import { useRef } from 'react';
       
       function FileInput({ name, value, onChange }) {
         const inputRef = useRef();
       	const [preview, setPreview] = useState("");
       
         const handleChange = (e) => {
           const nextValue = e.target.files[0];
           onChange(name, nextValue);
         };
       
         const handleClearClick = () => {
           const inputNode = inputRef.current;
           if (!inputNode) return;
       
           inputNode.value = '';
           onChange(name, null);
         };
       
       	useEffect(()=>{
       		if(!value) return;
       		const newPreview = URL.createObjectURL(value);
       		setPreview(newPreview);
       	}),[value];
         return (
           <div>
       			<img src={preview} alt="이미지 미리보기" />
             <input type="file" onChange={handleChange} ref={inputRef} />
             <button type="button" onClick={handleClearClick}>
               X
             </button>
           </div>
         );
       }
       
       export default FileInput;
  • 사이드 이펙트
    • 컴포넌트 안에서 외부의 환경을 바꾸거나 하는 행위를 사이드 이펙트라고 부른다. 보통 useEffect훅에서 사이드 이펙트를 발생시킨다. 정확히 말하면 사이드 이펙트를 발생시키기 위해서 useEffect를 사용한다.

    • 위의 이미지 미리보기 예제에서도 사이드 이펙트가 발생한다. 사용자가 이미지파일을 계속 바꾸면 메모리상에 오브젝트 url들이 계속 쌓이게 된다. 이렇게 메모리에 불필요하게 남아있는 것을 방지하려면 revokeObjectURL함수를 쓰면 된다.

      useEffect(()=>{
      	if (!value) return;
      	
      	const newPreview = URL.objectURL(value);
      	setPreview(newPreview);
      		
      	return () => {
      		setPreview();
      		URL.revokeObjectURL(newPreview); //메모리 해제
      	} //useEffect의 리턴한 콜백함수는 이전 useEffect의 사이드 이펙트를 정리하기 위해 다음 useEffect가 호출될 때 가장 먼저호출된다.
      }, [value]);
    • 페이지 정보 변경

      useEffect(() => {
        document.title = title; // 페이지 데이터를 변경
      }, [title]);
    • 네트워크 요청

      useEffect(() => {
        fetch('https://example.com/data') // 외부로 네트워크 리퀘스트
          .then((response) => response.json())
          .then((body) => setData(body));
      }, [])
    • 데이터 저장

      useEffect(() => {
        localStorage.setItem('theme', theme); // 로컬 스토리지에 테마 정보를 저장
      }, [theme]);
    • 타이머

      useEffect(() => {
        const timerId = setInterval(() => {
          setSecond((prevSecond) => prevSecond + 1);
        }, 1000); // 1초마다 콜백 함수를 실행하는 타이머 시작
        
        return () => {
          clearInterval(timerId);
        }
      }, []);

리액트에서 데이터를 서버로 보내기

영화 리뷰 글들을 모아놓은 사이트 기준으로 설명한다.

  • 글 작성
    1. Form 컴포넌트에서 onSubmit에 input value들로formData를 만들어서 post요청을 보낸다.
    2. 요청이 완료되면 Form 컴포넌트의 부모 컴포넌트에서 onSubmitSuccess를 만들어, 요청이 완료되면 할 일을 함수 안에 작성한다. 예를 들어 우리가 작성하고 제출한 글을 사이트에 반영하는 일 등이다.
  • 글 수정
    1. 수정 시, 업로드 폼을 그대로 재사용할 수 있다. 이 때 업로드 폼을 범용적으로 사용하기 위해 몇 가지 하드하게 작성된 함수들을 prop을 이용하여 유연하게 바꿔준다. 업로드때나 기존 글을 수정할 때나 상황에 맞게 사용할 수 있게 onSubmit과 onSubmitSuccess등을 prop으로 만들어준다.
    2. 부모에서 넘겨준 함수들을 가지고 Form 컴포넌트에서 수정을 한다.
  • 글 삭제
    1. delete 메서드를 이용하여 삭제요청을 보낸다.
    2. 리스폰스가 잘 오면 원래 화면에 있던 리뷰 목록을 setState로 삭제된 리뷰만 제외하고 업데이트 한다.

리액트 훅

프로그래밍에서 훅이란, 내가 작성한 코드를 다른 프로그램에 연결시켜 그 값이나 기능을 사용하는 것이다.

useState를 예로 들면, state는 사실 컴포넌트 안에 있는 값이 아니라 리액트가 따로 관리하는 값이다. 그래서 리액트가 관리하는 스테이트에 연결해서 마치 변수처럼 값을 사용하는 기능을 제공하므로 useState는 훅인 것이다.

  • 훅의 규칙

    1. 훅은 항상 리액트 컴포넌트 함수나 커스텀 훅 함수 안에서 실행되어야 한다.
    2. 반드시 함수의 최상위에서 실행되어야 한다. 반복문이나 조건문 등에서 사용할 수 없다.
    3. 리액트 훅은 항상 모든 렌더링마다 같은 순서로 실행되어야 한다.

    위와 같이 개발자 도구에서 스테이트 값을 보려고 하면 우리가 정한 이름이 아닌 state라고 나온다. 리액트 스테이트는 실행한 순서를 기억하여 값을 구분하기 때문에 순서는 항상 같게 만들어야 한다. 반복문이나 조건문을 사용하여 훅을 쓰면 이 순서가 바뀔 수 있는 가능성이 있기 때문에 안된다.

오늘의 나는 어떤 어려움이 있었을까?

공식문서를 찾아보고 어려운 개념을 이해하려고 하다 보니, 오늘 오전에 배운 개념들을 조금 까먹은 것 같다. 앞으로 너무 많은 개념을 한 번에 다 알아가려고 하지 말고, 고민해보다가 시간이 오래 걸릴 것 같으면 아카이빙 해두고 여우가 될 때 다시 찾아보는 방식을 택해야겠다.

내일의 나는 무엇을 해야할까?

  • 특강 듣기
  • 리액트 도커 환경 설정 하기 + vite로 리랙트 앱 만들기
  • 리액트 강의 완강하기

0개의 댓글