[코드캠프]7일차_TIL_🥶

윤성해·2023년 3월 21일
0

프론트엔드_TIL

목록 보기
7/27
post-thumbnail

🏷️ 수업목차

  1. 리액트의 데이터 흐름
  2. emotion에 props 던지기
  3. state 리렌더
  4. 컴포넌트 안에서 router 사용시 주의점
  5. 주소 설계하는 방법

1. 리액트의 데이터 흐름

리액트의 흐름은 단방향입니다. (부모 ➡️ 자식에게만 전달 가능)
부모 컴포넌트가 자식 컴포넌트에게 물려주는 변수/함수props 이었습니다.
props가 자식에게 넘겨주는 단계가 두단계 이상될 경우를 우리는 props drilling이 일어났다. 고 합니다!
props drilling이 과도하게 이루어지면 해당 prop가 어디서 내려지고 있는건지 찾는것이 힘들어집니다.
최대한 drilling이 안일어나게 해주는것이 가독성과 유지보수 면에서 유리합니다!

2. emotion에 props 던지기

emotion 으로 만들어진 태그에도 props를 전달할 수 있습니다.
즉, 특정 태그를 클릭하는 등의 행위가 일어나면 props를 활용하여 css를 변경할 수 있습니다.

  • javascript 부분
import { useState } from 'react';
import { Test } from '../../src/test2';

export default function Test2() {
	const [isTrue, setIsTrue] = useState(false);

	const handleOnClick = () => {
		setIsTrue((prev) => !prev);
	};

	return (
		<>
			<Test onClick={handleOnClick}>클릭하면 색이 왔다 갔다</Test>
		</>
	);
}
  1. import로 useState 해주고
  2. useState 만들어준다음 -> const [isTrue, setIsTrue] = useState(false)
    // isTrue에는 기본적으로 false가 담겨있다는 뜻
  3. 밑에 handleOnClick 함수에서 setIsTrue에 ((prev) => !prev)-> 클릭뭐시긴가? 를 담아주고
  4. 위 setIsTrue 의 값이 isTrue에 담긴다.
return (
		<>
			<Test isTrue={isTrue} onClick={handleOnClick}>클릭하면 색이 왔다 갔다</Test>
		</>
	);
  1. 밑부분에 isTrue={isTrue} 요거 추가해서 emotion으로 넘길 수 있도록 준비

오늘 수업 실습

  • state의 원리를 이해하면서 인풋창에 값 채워지면 버튼이 활성화되게 해보기!
  1. 하드코딩하기(이렇게하면 항상 설정한 값이 나온다.)
  2. 조건에 따라 색이 바뀌도록

3. state 리렌더

setState는 비동기로 작동합니다.
동기로 작동한다면 변경될때마다 바로바로 렌더링을 하기때문에 비효율적입니다.

리액트에서 해결방법 ➡️ 임시저장공간에 변경되는 값들을 모아두었다가 함수가 끝나면 마지막꺼 놔두고 그전꺼 한번, 마지막 한번해서 총 두번! 한번에 렌더링 됩니다.

💡리렌더가 되는 상황
1. 새로운 props가 들어올 때
2. 부모 컴포넌트가 렌더링 될 때
3. 강제 업데이트(forceUpdate)가 실행될 때
4. state가 변경될 때

💡setstate의 비동기적 특성
아래의 버튼을 클릭하면 어떤값이 나올까요?

→ 정답은 1입니다.
이는 setState가 비동기적 특성을 갖기때문입니다.
(동기적으로 작동했다면 코드를 읽고 바로 내려가서 또 1을 더하고 또 1을 더해 3이 나왔을 것 입니다.)

export default function stateTest(){
const [value,setValue]=useState(0)

const onClick = () => {
    setValue(value+1)
    setValue(value+1)
    setValue(value+1)
  }

  return (
    <div className="App">
      <button onClick={onClick}>+</button>
      <h1>{value}</h1>
    </div>);
}
  • 퀴즈풀때 문제-> 인풋값의 문자가 2글자 이상부터는 색이 바뀌었는데 1글자쓰면 안되었었음. event.target.value 까지는 한글자가 들어가지만 그게 title, writer, contents에 담기기 전이었어서 if문 안에 (writer && title && contents) 이렇게 쓰면 한글자면 담겼다고 인식이 안되었던 것임. 그래서 사진처럼 (event.target.value) 로 넣어준다!

해결방법은

state 사용하면 눈에는 변경되는 것 처럼 보이지만 , 모든 값이 재설정 되면서 초기화가 되는 것. use 로 시작하는 애들은 유지가 되고, 다른애들은 다 리셋된다. 그래서 걔만 바뀌는 것 처럼 보이는것이었다.
비효율 적이지만 나중에 최적화하는 방법을 배운다.

💡참고! -요고도 리렌더링 2번 됨
const {data}= useQuery()
1. 최초 언디파인 일 때, 그리기
2. 데이터 받아오면 변경된 스테이트로다시 그리기
-> 그러므로 datastate임.

3. 컴포넌트 안에서 router 사용시 주의점

router.query 가 반드시 페이지 안에서만 사용되는건 아닙니다.
라우터가 연결된 모든 컴포넌트에서 사용이 가능합니다.

따라서 페이지 컴포넌트에서도 가능하지만, BoardDetail 컴포넌트에서도 사용이 가능합니다.

router 사용시 주의할 점

단, 라우터가 있는 컴포넌트를 재사용 하실때는 다이나믹 라우팅 폴더[aaa]가 있는지 잘 확인하고 재사용 해야합니다. 폴더가 없는곳에 router.query를 가지고 가면 오류가 뜨게 됩니다.

💡 router.query를 사용할 때는
→ 콘솔에 router.query를 찍어보고, 안에 뭐가 있는지 확인 후 필요한 걸 꺼내서 사용하시는 게 좋습니다.

수업내용
보드라이트 컨테이너, 프리젠터
같이봄

여기서 라우터 푸쉬로 해당하는 페이지로 이동하려 할 때 비정상적인 사례가 있다.

위 사진은 보드디테일.컨테이너임

이렇게 위 경우에 router.query.boardId 라고 라우터 설정하면 뉴-보드에 나올까?
라우터가 실행될 때는, 자식컴포넌트 들이 앱까지 다 적용 되었을때 실행된다.

위 이미지는 에러난다. 주소창에서 뭔가 뽑아오는 애들이 있는 애들이 내용에 있으면 어떤페이지에 import 되는지에 따라서 (보드아이디가 있어야함) 열릴지, 안열릴지 임
(router.query.어쩌구) 있는애들은 아무페이지에서나 다 작동하지는 않는다.

4. 주소 설계하는 법

게시물 목록, 게시물 등록, 다이나믹 라우팅 표기, 게시글 수정하기 주소

게시물 목록 : /boards
게시물 등록 : /boards/new
다이나믹 라우팅(특정 게시물 조회)

  • /boards/: id → :으로표기
  • /borads/{id} → {}으로 표기

게시글 수정하기

  • /boards/:id/edit
  • /boards/{id}/edit

❗️ 수정은 특정 게시물을 수정하는 것이기 때문에 특정 게시물의 id로 다이나믹 라우팅을 해줘야 합니다.

💡실무에서 사용하는 변수, 폴더 네임

실무에서 변수명이나 폴더, 컴포넌트명을 마음대로 하게되면 다른사람이 코드를 보게되었을때 어떤 부분인지 알기 어렵습니다.
따라서 실무에서는 해당 변수가 무엇을 위한 변수인지, 해당 컴포넌트나 폴더가 어떤 부분인지 명시하는게 좋습니다.

// 숫자를 카운트 하는 변수를 선언하기
let count;
// 날짜 관련 변수 선언하기
let Date;
//메인 컴포넌트를 만든다면
export default function Main(){
		return( ...코드 )
}

5. 목록을 보여주기위한 기초작업 - Map

  1. for문은 실무에서 안쓴다! ➡️ map도 반복문
  2. 키는 인덱스를 싫어해
const classmate = ["철수","영희","훈이"]

classmate.map((item)=>(item+"어린이"))
=> (3)["철수어린이","영희어린이","훈이어린이"] 이렇게 됩니다.

map안에서 사용되는 item은 classmate의 원소들이 들어갈 파라미터(매개변수) 입니다.
파라미터의 네이밍은 마음대로 정하셔도 무방합니다.

객체가 원소인 배열에서도 map을 이용하여 객체를 가공할 수 있습니다.

const classmate = [{ name: "철수" }, { name: "영희" }, { name: "훈이" }];

  //item.name => "철수","영희","훈이"
  //school 속성을 일괄적으로 추가해주도록 하겠습니다.
  classmate.map((item) => ({
    name: item.name + "어린이",
    school: "떡잎유치원",
  }));

	// 결과
	=> (3)[
			    { name: "철수어린이", school: "떡잎유치원" },
			    { name: "영희어린이", school: "떡잎유치원" },
			    { name: "훈이어린이", school: "떡잎유치원" },
			  ];

❗️ 화살표 함수 안 중괄호안에 아무것도 없으면, 소괄호로 바꿀 수 있고 특별한 의미가 없다면 생략도 가능합니다. 바디에 적히는 코드가 한줄 이상이거나 리턴값이 객체라면 생략이 불가능합니다.
❗️화살표 함수 () ⇒ {} vs () ⇒ ()
→ 소괄호 ()로 감싸진 부분이 return 됩니다.(return문을 작성하지 않아도 리턴 됩니다.)
반면 중괄호{}로 감싸진 함수는 return문이 없다면 반환값이 없습니다.

  • map.(el => el + "어린이") 이렇게 괄호 생략 가능.

    얘는 소괄호 생략한건데 생략하니까 리턴이 없는 이상한 로직이 되었다.
  • 생략 불가능!

1) map을 html에 활용하기

실제 프로젝트에서는 컴포넌트의 리턴값안에서 많이 사용하게 됩니다.
map을 리턴값 안에서 사용하기위해 {}으로 감싸서 사용합니다.(return 안에 자바스크립트를 가지고 오기 위해서 {}을 사용하는 것 입니다.)

// RenderMap 컴포넌트
const RenderMap = () => {
  const classmate = ["철수", "영희", "훈이"];

  return (
    //중괄호를 이용해 자바스크립트를 컴포넌트의 return값 안으로 데리고 들어온 것 입니다.
    //리턴되어야 할 값이 한줄이기때문에 괄호생략이 가능합니다.
    <div>
      {classmate.map((item) => (
        <div>{item}어린이</div>
      ))}
    </div>
  );
};
export default RenderMap;

2) filter의 사용방법

filter로 원하는 값을 출력할 수 있습니다.

const num = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

num.filter((item) => item <= 8);
=> (8) [1, 2, 3, 4, 5, 6, 7, 8]

filter 안에서 사용되는 item 역시 배열의 원소가 들어갈 파라미터 입니다.

💡 map과 filter의 결과물 차이점
map은 배열의 길이만큼 값이 나오지만, filter는 조건에 따라 배열의 길이보다 적게 나올 수 있습니다.
💡 진행 과제
짝수값의 number를 가진 객체를 출력해주기.

3) refetchQueries

refetchQuery는 기존에 받아왔던 데이터가 변경 되었을 경우 최신 데이터로 다시 fetch 해주기 위해 사용됩니다. 과제 실습 중, 삭제 Mutation을 사용하여 데이터를 지웠지만, UI에는 새로고침을 해야만 삭제가 된 것을 볼 수 있었습니다. 삭제가 되자마자 볼 수 있도록 만들어 줍니다.

//refetchQueries를 이용해 최신 데이터 받아오기

const deleteBoard = async () => {
	try {
		const result = await deleteBoard({
			variables: {
				boardId: event.target.id,
			},
			refetchQueries: [
				{
					query: FETCH_BOARDS
				},
			]
		});
	} catch (error) {
		console.log(error);
	}
};

위의 코드를 자세히 살펴보시면 useMutation 함수 안에서 refetchQueries라는 키가 있다는 것을 알 수 있습니다. refetchQueries는 Apollo에서 제공하는 기본 기능입니다.

refetchQueries는 배열로 시작하여, 그 안에 어떤 query를 하고, 그 query의 variables가 무엇인지 다시 설정해주면 Mutation이 성공적으로 끝났을 경우 refetchQueries를 실행시켜줍니다.

💡refetchQueries에서의 variables
→ 기존의 fetch 부분에서 보내준 variables가 없다면 따로 적지 않아도 되지만, 보내줄 variables가 있다면 refetchQuerise부분을 아래와 같이 적어주세요

refetchQueries: [{ query : FETCH_BOARDS,
                   variables : { 기존의 fetch때 보내준 것} }]

모든 Mutation 이후에 refetchQueries를 사용하는 것은 아닙니다. Mutation 이후 변경된 데이터를 받아와야 할 경우에 사용합니다.

4) map의 index를 key로 사용시 발생하는 문제점

map을 사용할 때, key값을 부여하지 않는다면 아래와 같이 오류가 발생합니다.

💡 해결 방법
중복되는 값이 없는, 고유의 키값으로 설정해줍니다.
근데 인덱스로 키값 설정하면 뭐가 문제냐면 체크박스가 안지워진다?
-> 이유는 인덱스 값으로 설정하고 지우면 뒤에있던 애가 올라와서 다시 그 인덱스값이 되기때문에 XXX

api를 확인하고, 고유의 키값을 확인해줍시다!

index를 key로 주어도 괜찮은 상황

index를 key로 줘도 괜찮은 상황이 있습니다. 아래 3가지 조건을 만족하면 됩니다.

  1. 정적인 데이터. 계산되지 않고 변경되지 않는 데이터
  2. map에 있는 모든 데이터에 id가 없을 경우
  3. 데이터가 재정렬되거나 필터링 되지 않는 경우. (계속 그 자리 그대로)

위의 3가지를 만족한다면 index를 key로 사용해도 안전합니다.
하지만 서버에서 받아오는 데이터라면 안됨!!


🤷🏻‍♀️ 궁금한 것


❗️알게된 것


✨ 느낀점

profile
Slow and steady wins the race.

0개의 댓글