React로 미니블로그 만들기

seul_velog·2022년 5월 8일
0

✍️ 리액트 기초 문법 & 블로그 제작 실습(강의)을 통해서 작성해본 코드들과 복습+기억해두고 싶은 내용들을 메모해 보았다.


1. JSX에서 styling하기

  • 아래 예시처럼 바로 style 속성을 줄 수 있다.
  • font-size 는 - 를 뺄샘으로 보기 때문에 fontSize 로 적는다.
return(
  <div className='App'>
    <div className='blue-nav'>
  	  <h4 style= {{color: 'blue', fontSize: '16px'}}
		>블로그</h4>
	  </div>
    </div>
  );



2. state, setState와 onClick

State

  • 자료를 잠깐 저장할 땐 변수를 이용한다.
  • 자료를 잠깐 저장하기 위해 state를 써도 된다.
  • 자주 변경될 것 같은 (자료)html 부분은 state로 만들자.

📌 state는 어디에 만들까?
state를 사용하는 컴포넌트 중 최상위 컴포넌트에 만들기


State 변경하는법

  • 등호로 변경 금지 ( ex. state = state+1 ) → html 반영 되지 않는다.
  • state변경함수(새로운state) 와 같이 작성한다.

State 변경함수 특징

  • 기존 state == 신규state 의 경우 변경 X → 에너지 절약
    : reference data type ( 이 부분은 얕은복사의 이론을 알고 있어야 응용가능하다. 🤔 ) 얕은복사와 깊은복사

  • ❗️ arr, obj 복사할 때 주의
    : 변수 & 변수 2 화살표(참조)가 같으면 변수1 == 변수2 비교시 true 이며, 결국 업데이트 되지 않는다.

  • 따라서 아래처럼 해줘야 독립적인 array 생성, 즉 리액트가 렌더링을 다시 해줄 수 있다.
    let copy = [...copy할 것]
    : ‘화살표를 다르게 지정' 과 동시에 + 스프레드 문법특징( ... 괄호를 펼친다.) + []괄호로 다시 씌워준다.) 의 의미이다. 😀

  • 즉, state가 array 나 object 면, 독립적 카피본을 만들어서 수정해 주자. (문자나 숫자 데이터는 괜찮다고 한다.)


onClick 사용법

  • onClick={} 안에는 함수이름을 넣어야 한다. console.log(’hi’) 이런식으로 JS 바로 쓸 수 없다.
  • 혹은 함수 만드는 문법(정의)를 바로 넣어도 괜찮다.
    • function(){console.log(’hi’)}
    • onClick={()⇒{ ... }}



3. component ✨

컴포넌트 만드는 법

  1. function을 만든다. (다른 function 바깥쪽에 만들고 첫글자는 대문자로 한다.)
  2. return() 안에 html 담는다.
  3. <함수명></함수명> 과 같이 쓴다.

어떤걸 컴포넌트로 만들면 좋은가

  • 반복적인 html 축약할 때
  • 큰 페이지들
  • 자주 변경되는 것들
    (컴포넌트의 단점: state를 가져다 쓸 때 불편한 점이 생길 수 있다.)

📌 동적인 UI 만드는 단계

  • 동적인 UI : 탭, 모달창, 경고창, 툴팁, 햄버거메뉴 등

1. html + css 로 미리 디자인 완성한다.
2. UI의 현재 상태를 state로 저장한다.
3. state에 따라 UI가 어떻게 보일지 작성한다.
4. 스위치를 작동한다. (필요시 상태변경하기. onClick 등)

디자인 만들기

function App(){
	return(
		<Modal />
	)
}

function Modal() {
  return (
    <div className='modal'>
      <h4>제목</h4>
      <p>날짜</p>
      <p>상세내용</p>
    </div>
  );
}

UI 상태 저장하기

  • 여기서 필요한 상태는 보일지 안보일지에 대한 두 가지 상태이다.
let [modal, setModal] = useState(false); // 스위치 역할

state에 따라 UI가 어떻게 보일지 작성한다.

  • 조건문을 활용한다. → {}를 사용, js문법을 이용한다.
    but! 자바스크립트식 if 문을 JSX에서는 사용할 수 없다.
  • 삼항연산자를 활용한다. → 조건식 ? 참일 때 실행할 코드 : 거짓일 때 실행할 코드
{modal === true ? <Modal /> : null}
  • ‘’ 텅 빈값 혹은 null (비어있는 html용으로 자주 사용) 을 입력해 줄 수 있다.

스위치를 작동시킨다. (필요시 상태를 변경한다)

<button
  onClick={() => {
     modal === false ? setModal(true) : setModal(false); // 내가 짠 코드
     setModal(!modal); // 솔루션(훨씬 편함)
  }}
>



4. map() 반복문 사용

map()

  • 모든 arr 자료 뒤에는 map() 을 사용할 수 있다. 콜백함수를 넣어준다.

  • JSX 내부 JS식 코드{} 중괄호 안에서는 if처럼 for 도 사용할 수가 없다.
    ( 굳이 for을 쓴다면 JSX안이 아닌 바깥에서 사용할 수도 있다. )

  • 비슷한 html 반복생성 하려면 map() 을 사용하면 된다! 😀

  1. array 자료 개수만큼 함수내부 코드를 실행 해준다.
  2. 함수의 파라미터는 array안에 있던 하나하나의 데이터이다.
  3. return의 로직을 array로 담아준다.
    +) 유용한 파라미터 2개를 사용한다. (두 번째는 정수 인덱스 0,1,2... 이다.)

✍️ 실습중 어려웠던 부분

→ map으로 돌린 리스트들 각각의 state를 setState 함수를 통해서 개별 업데이트 시켜주는 것

let [like, setLike] = useState([0, 0, 0]); // 1)

{/* ------------ 리스트 ------------*/}
      {title.map((title, i) => (
        <div className='list' key={i}>
          <h4>
            {title}
            <span
              onClick={() => {
                let copyLike = [...like]; // 2)
                copyLike[i] = copyLike[i] + 1; //3)
                setLike(copyLike); // 4)
              }}
            >
              👍
            </span>
            {like[i]}
          </h4>
          <p>217일 발행</p>
        </div>
      ))}

1) [like1, setLike1][like2, setLike2]... 이렇게 각각 하는 것은 비효율적이므로 배열에 상태를 다 담는다.

2) 따라서 객체배열데이터의 상태는 ... 스프레드 연산자로 카피본을 만들어주는 것 잊지말기!

3) 필요한 작업을 하기 → 이 부분에서 막혔다. 😵
: onClick에서 setState를 작성하는 순간자체를 맵을 생성하는 단계인 것처럼, 마치 for문을 돌리는 단계처럼 생각해버렸다.
이미 map은 개별적으로 list 들을 다 돌려서 하나하나하나 만들어진 상태이다. 이상태에서 내가 onClick 을 발생 시킨 것은 이미 정해져있는 index의 해당 list 일뿐. 따라서 onClick 내부의 함수는 내가 누른 index와 일치된 상태일 것이다. (이렇게 이해를 했다.) 더 코드를 접해보자. 🤔

4) 간단하게 3)에서 수정된 부분으로 업데이트 해주면 된다.




5. props

부모 → 자식 state 전송하는 법

  1. < 자식컴포넌트 작명 = { state 이름 } >
  2. 자식 컴포넌트에서 props 파라미터 등록 후 props.작명 사용
  • props 로 일반 문자도 전송가능 ex. ) <Modal text=”0” />
  • props는 여러가지 형태의 데이터를 여러개 모두 전달 가능하다.
    • <Modal 글제목={변수명}> 일반 변수, 함수 전송도 가능
    • <Modal 글제목="블로그"> 일반 문자전송은 중괄호 없이 사용 가능

✍️ 과제1) 자식 컴포넌트에서 부모컴포넌트의 state 변경하기

1)

// App
{modal === true ? <Modal title={title} setTitle={setTitle} /> : null}


// Modal
function Modal(props) {
  return (
    <div className='modal'>
      <h4>{props.title[0]}</h4>
      <p>날짜</p>
      <p>상세내용</p>
      <button
        onClick={() => {
          const titleCopy = [...props.title];
          titleCopy[0] = '여자코트추천';
          props.setTitle(titleCopy);
        }}
      >
        글 수정
      </button>
    </div>
  );
}

2)

function App() {
  let [title, setTitle] = useState(['여행지 맛집', '유럽여행', '여행 경비']);

  const changeState = (newTitle) => {
    const copyTitle = [...title];
    copyTitle[0] = newTitle;
    setTitle(copyTitle);
  };

return()
 ...
	{modal === true ? (
        <Modal title={title} setTitle={setTitle} changeState={changeState} />
      ) : null}
...
  }


function Modal(props) {
  return (
    <div className='modal'>
      <h4>{props.title[0]}</h4>
      <p>날짜</p>
      <p>상세내용</p>
      <button
        onClick={() => {
          props.changeState('여자코트추천');
        }}
      >
        글 수정
      </button>
    </div>
  );
}
  1. state 변경 함수changeState를 먼저 만든 후
  2. 자식 컴포넌트에게 props로 changeState를 내린다음
  3. 이런식으로 자식 컴포넌트에서 onClick 을 이용, 원하는 데이터를 넣어서 changeState를 호출

solution

<button
    onClick={() => {
      props.setTitle(['여행지 포토스팟', '유럽여행', '여행 경비']);
    }}
  >
    글 수정
</button>

✍️ 과제2) 내가 놓친 포인트

  1. 결국 모달 제목도 변동되는 UI 이다. → 컴포넌트 파트의 📌 동적인 UI 만드는 step 을 따르자.
  2. step.2( UI의 현재 상태를 state로 저장 ) 부분
  • 상태의 조작을 복잡하게 ['여행지 맛집', '유럽여행', '여행 경비'] 생각했다는 점이 미스였다.
  • modal UI를 구현하듯, true 일때 보여주고, false일때 보여주지 않는 다는 일종의 스위치 개념으로 다시 바로잡자.
    let [modalTitle, setModalTitle] = useState(0);
    → 상태는 총 세개. 즉 1 2 3 세개의 상태가 필요할 것이며 초기 상태로 0을 두자. 필요시 1, 2 그리고 다시 0 으로 바뀔 수 있도록 set 설정을 하면 될 듯한 느낌을 갖자.
  1. 모달 함수로 와서 해당 UI가 동작할 부분을 위와 연관 지어서 유심히 살펴보자

    // 이렇게 구현되면 좋겠다 라는 생각을 가진 상태로
    <h4>{props.title[0]}</h4> // 첫 번째 제목의 타이틀
    <h4>{props.title[1]}</h4> // 두 번째 제목의 타이틀
    <h4>{props.title[2]}</h4> // 세 번째 제목의 타이틀
    
    // 이렇게 응용할 수 있어야한다 !!!
    // 여기서 modalTitle은 위에서 상태를 설정했던 숫자 0,1,2를 나타낼 수 있음을 !!
    <h4>{props.title[props.modalTitle]}</h4> 
  2. 그럼 이제 실제 요소에 onClick을 동작시켰을 때, setState 함수를 이용해서 원하는 값을 동적으로 갖도록 상태를 변경하는 것에 대해 고민하자. ( 이 부분은 바로 해결했다.😀 )

    // App 컴포넌트에서 onClick을 발생시킬때 스테이트 변경을 동적으로 잘 처리하고
    <h4
      onClick={() => {
        setModal(!modal);
        setModalTitle(i); // i로 설정한다.
      }}
    >
    
    // 모달 컴포넌트에서는 바뀐 state를 고대로 props으로 받아와서 적용한다.
    <h4>{props.title[props.modalTitle]}</h4>





6. input

input의 여러 타입들

<input type='text' />
<input type='checkbox' />
<input type='range' />

<select></select>
<textarea />

onChange

  • <input> 에 입력시 코드를 실행하고 싶으면 onChange / onInput 을 사용한다.
  • <input onChange={()=>{???}} /> : 유저가 인풋안에 값을 타이핑 할 때마다 안의 코드가 실행된다.
  • 이러한 이벤트 핸들러는 onClick = {} , onChange = {} , onInput = {} 등 매우 많다.
  • onMouseOver = {} : 마우스를 가져다 대면 안의 코드 실행
  • onScroll = {} : 해당 태그에 스크롤바가 있다면 스크롤바를 조작할 때마다 안의 코드 실행 등 필요할 때 잘 찾아서 쓰자✍️

input태그 값 가져오기

let [value, setValue] = useState('');
...

<input
  onChange={(e) => {
    setValue(e.target.value); // 비동기처리
    console.log(value); // 처음 출력시 빈 문자열
  }}
/>
  • 이렇게 가져온 값은 보통 변수/state에 저장해서 사용하는 경우가 많다.

  • e.stopPropagation();
    : 상위 html로 퍼지는 이벤트버블링을 막고 싶을 때 사용한다.

  • state 변경함수는 늦게 처리된다(비동기처리)
    : 따라서 먼저 출력하는 것은 console.log 이므로 처음에는 ‘ ’ 빈 문자열이 출력된다.


✍️ 과제3) 게시글 추가 & 삭제 구현하기

<input
  onChange={(e) => {
    setValue(e.target.value);
  }}
/>
<button
  onClick={() => {
    let titleCopy = [...title];
    titleCopy.unshift(value);
    setTitle(titleCopy);
// 아래는 추가로 구현한 부분이다.
    let likeCopy = [...like];
    likeCopy.push(0);
    setLike(likeCopy);
  }}
>
  게시글 추가
</button>
  • 잘 구현됐다. 추가로 👍버튼 옆의 like 상태도 넣어주었다. 그런데 이렇게 일일히 넣어줘야할까..? 🤔

✍️ 내가 삭제를 못 푼 이유..

map() 함수를 처음에 만들 때 받아오는 아이템과 인덱스 그리고 배열 부분

let [title, setTitle] = useState(['여행지 맛집', '유럽여행', '여행 경비']);

{title.map((title, i) => ( // title 을 넣었기 때문
        <div className='list' key={i}>
  • 인자의 아이템을 받아오는 첫번째 파라미터 자리에 state와 같은 변수명을 사용했다.
    따라서 map() 함수 내부에서 title을 출력했더니 ['여행지 맛집', '유럽여행', '여행 경비'] 의 정상적인 state 배열이 출력되질 않았다.. 이문제를 여기까지 와서 깨달은 이유는 바로

  • 겉보기에는 멀쩡했기 때문이다. 위의 제목도 map()함수로 넘어온 첫번째 요소 즉, 아이템 {title} 을 사용했으니 똑바로 적용 되었고, 콘솔로 title을 출력하면 원래라면 state 배열이 나와야 하겠지만 말그대로 map() 함수의 아이템이 출력되었기 때문에 의심을 하지 않았다. ⚡️

  • 하지만 이상태로 onClick 이벤트 내에서 state 복사본을 만든다면..?
    onClick = {()⇒{ let copy = [...title]}} ← 바로 여기서 ...title 문법이 에러가 날 수 밖에없다! 위의 콘솔을 보면 알겠지만 map()의 아이템 즉, 각 문자열이 출력되는데 배열형태가 아니니 배열 복사본을 만드는 것 자체가 말이 안되었던 상황이었다. 😵
    따라서 이렇게 계속 코드를 짜게되면 이벤트 동작시 아래와 같은 화면을 만나게 된다.

  • 물론 계속 콘솔 출력하며 코드를 짰으니 저 부분이 배열이 아니므로 복사본을 만들지 않고 다른 방향으로 어떻게 짤지 계속 고민을 했고, map() 함수 내에서 아이템을 각각 받아오므로 배열을 가질 수 없었던 나는.. title state의 배열의 복사본을 사용하려면 map()함수 밖으로 나가야겠다고 결론을 내리고 열심히 삽질했다. 😂
  • 그냥 첫 번째 요소의 자리를 a 라던지(솔루션 처럼) _ 등으로 사용했으면 해결될 문제였다. (그부분을 실제로 사용하지 않고서도 코드를 짜는게 가능했다.)

→ 결론적으로 map() 함수의 첫번째 파라미터 요소의 아이템을 useStatetitle과 동일한 이름으로 지어서 생긴 문제였다. title state의 배열을 가져와야 그 배열을 복사하고, 수정하고, setState를 적용할 수 있었는데 나는 계속 문자열 상태였기 때문에 솔루션 로직을 알고 있었더라도, 실행할 수가 없었던 상황 :) 그래도 다음 부터는 실수를 안하겠지! ✍️

📌 아래와 같이 작성하면 된다.

<button
  onClick={(e) => {
    e.stopPropagation();
    let copyTitle = [...title];
    copyTitle.splice(i, 1);
    setTitle(copyTitle);
  }}
></button>




reference)
codingapple

profile
기억보단 기록을 ✨

0개의 댓글