위스타그램 개발노트(React)

Jihyun-Jeon·2022년 6월 16일
28

Project  및 활동

목록 보기
2/7

※ 자바스크립트로 진행한 위스타그램
https://velog.io/@jhplus13/%EC%9C%84%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C%EB%85%B8%ED%8A%B822

✅ 초기세팅시

  • reset.scss, common.scss의 위치는 index.js에서 한번만 하면 됨.

  • import 할 때도 순서가 있다.
    :회사마다 다르겠지만 체계적인 관리를 위해 순서를 정해놓고 쓰는게 좋다.

    • 1.라이브러리
      • React 관련 패키지
      • 외부 라이브러리
    • 2.컴포넌트
      • 공통 컴포넌트 → 먼 컴포넌트 → 가까운 컴포넌트
    • 3.함수, 변수 및 설정 파일
    • 4.사진 등 미디어 파일(.png)
    • 5.css 파일 (.scss)



✅ 아이디 값과 비밀번호 값을 한 state로 관리하기

🔆 코드

// 1. id, password 입력값 받아오는 값을 한 객체로 만듦
const [loginInfo, setLoginInfo] = useState({ name: '', password: '' });

// 2. 계산된 속성명 : 키를 직접 지정하기 않아도 변수명을 통해 키 값을 받아올 수 있다
 const onInput = e => {
    const { name, value } = e.target; 
   // const name = e.target.name , const value = e.target.value인 것임.

    setLoginInfo(prev => {
      return { ...prev, [name]: value };
    });
 }
 
 // jsx부분
  <form id="loginForm" onSubmit={onSubmit}>
     <input type="text" name="name" onInput={onInput}/>
     <input type="password" name="password" onInput={onInput} />
     <button type="submit"> 로그인 </button>
  </form>

🔆 계산된 속성명 사용

이를 이용해 아이디 input창과 비번 input에 각각 state를 주지 않고 하나의 객체형태로 state로 관리하였다.

  • name 속성은 input태그에서만 사용할 수 있음.
  • input태그에 name을 주어 onInput이벤트 발생시 어느 input창에서 이벤트 발생했는지 알 수 있다.

🔆 구조분해할당

구조분해할당 사용하여 코드를 간결하게 쓰기

  • 예제1
const {value, name} = e. target;

// e.target.value를 value라는 변수로 만들어 쓸 수 있음
// e.target.name를 name이라는 변수로 만들어 쓸 수 있음
  • 예제2
const [userInfo , setState] = useState({id:"", pw:""})

const {id , pw} = userInfo

// 즉,
// id = userInfo.id 값을 쓰는 것임
// pw = userInfo.pw 값을 쓰는 것임



✅ 불필요한 state 줄이기

공식문서 : https://ko.reactjs.org/docs/thinking-in-react.html#step-3-identify-the-minimal-but-complete-representation-of-ui-state

state의 업데이트는 렌더링을 발생시킨다.
만약 state로 관리하지 않아도 되는 값을 state로 관리할 경우, 일어나지 않아도 되는 렌더링이 발생한다.
따라서 불필요한 state값은 쓰지 않아야 한다.

🔆 문제 코드

  const [inputValue, setInputValue] = useState("");
  const [searchList, setSearchList] = useState(names);

  const onInput = e => { 
    setInputValue(prev => (prev = e.target.value));
    setSearchList(prev => {
      if (searchValue === '') {
        return [...names];
      }
      return names.filter(el => el.name.includes(searchValue));
    });
  };

return(
   <input type="text" placeholder="검색" onInput={onInput}/>

  {/* 검색창 */}
     <div id="searchId">
      {searchList.map((el, idx) => { 
        return (<div className="idBox" key={idx}>
                      <p>{el.name}</p>
                       <p>{el.korean}</p>
               </div>);
            })}
      </div>
)

👉 시도

input창에서 입력한 검색어를 받아오기 위해 state을 만들고,
names라는 검색창에 나열될 리스트를 배열형태로 state를 만들어
names에서 검색어를 포함하고 있는 것만 filter로 걸러서 리렌더 해주려 했음.

👉 문제

  1. e.target.value를 별도의 state로 관리하기 보단, 바로 변수로 받아 사용할 수 있음.

  2. 하나의 함수나 메서드에는 하나의 동작만 하는게 좋음

  3. 더군다나, onInput에서 inputValue을 업데이트하는 시기는 onInput 콜백함수의 본문이 다 끝난 후에 진행되기 때문에, setSearchList은 이전값을 받아오고 있어서 e.target.value값이 하나씩 밀리게 됨.

    ※ useState 의 setState는 비동적으로 동작 포스팅 참고: https://velog.io/@jhplus13/%EC%9C%84%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C%EB%85%B8%ED%8A%B8React

👉 해결

검색어를 받아오기 위한 useState는 불필요한 state이기 때문에 삭제하고,
onInput 이벤트 발생시 실행되는 콜백함수 내에서 e.target.value를 하나의 변수로 받아 사용함.




✅ 반복되는 구조는 map을 통해 렌더하기

반복되는 구조를 그릴땐, 배열에 데이터를 담고 map으로 배열을 순회하면서 동일한 구조를 반복하여 렌더하는게 좋음.

🔆 코드

const [commentList, setCommentList] = useState([
    { id: new Date().getTime(), txt: '댓글썻당!!' },
    { id: new Date().getTime(), txt: '댓글 두번썻당!' },
  ]);

// jsx 부분
return(
 <ul id="comments">
          {commentList.map((el, idx) => {
            return (
              <Comments
                key={el.id}
                txt={el.txt}
                userNum={idx}
                idNum={el.id}
                setCommentList={setCommentList}
              />
           );
       })}
  </ul>
)

댓글은 동일한 구조의 ui가 반복하여 여러개가 생성될 것임.
따라서 댓글에 대한 데이터를 한 배열(commentList 값으로)에 담고,
해당 데이터를 map으로 순회하면서 동일한 구조를 반복하여 렌더해줌.

이때 댓글 한줄을 렌더하는 기능을 별도의 컴포넌트(Comments)로 만들었음.
Comments컴포넌트는 댓글 한 줄에 대한 좋아요 기능과 삭제 기능을 구현하는 기능이기 때문에 별도의 컴포넌트로 분리했다.

🔆 unique한 key

1. key를 쓰는 경우

  • jsx에서 "배열"을 렌터링 할 땐, key를 prop으로 항상 넣어줘야 함!
  • map, filter처럼 리턴값이 배열이고 , 이 배열이 jsx로 렌더링 되는 것일때 씀.

2. key를 쓰는 곳은 ?

  • 배열을 도는 곳에서 써야 함. (map,filter 메서드 안에서)
  • 리턴되는 부분의 최상위 영역에 key를 부여해줘야 함.

3. unique한 key값을 주는 법

  • index값으로 key값을 주면 안좋음. 해당 배열이 수정되면 index번호가 달리 부여되기 때문에

  • 배열을 바꿀 필요가 없고, 한번 렌더된 후에도 배열이 수정될 가능성이 정말 없을때도 key를 써야한다.
    이 땐 key값에 idx를 줘도 무방함.(그래도 되도록 안쓰도록..)

  • key값으로 new Date().getTime() 을 쓰는것도 안좋다.
    : state가 업데이트되서 컴포넌트가 재실행될때마다 new Date.getTime이 새로 실행되니까,
    이전 ms값과 다르게 나오기 때문에! (실무 : 백엔드에서 받은 데이터로 쓴다.)




✅ state 끌어올리기

🔆 State 끌어올리기란?

prop의 값으로 “setState 함수 자체”를 넘겨주어, 자식 컴포넌트에서 부모 컴포넌트에 있는 state값을 바꿀 수 있다.

(※ prop의 값은 어느 데이터든 상관 없음 - “컴포넌트 자체” , "함수 자체" 등... 다 가능함. )

🔆 코드

1. Feed 컴포넌트에서 댓글 정보를 담고있는 commentList라는 state가 있음.

function Feed({ id, writerId, writerImg, feedImg, feedText }) {
  const [commentList, setCommentList] = useState([
    { id: 1, txt: '댓글1' },
    { id: 2, txt: '댓글2' },
  ]);
 
  return(
    <ul id="comments">
       {commentList.map((el, idx) => {
            return (
              <Comments
                key={el.id}
                txt={el.txt}
                userNum={idx}
                idNum={el.id}
                setCommentList={setCommentList} 
  			 />
           );
        })}
    </ul>
  )
}

2. Comments 컴포넌트에서, 부모컴포넌트에 있는 commentList를 업데이트 시키기 위해, setCommentList를 prop으로 받아 자식 컴포넌트에서 부모 컴포넌트의 state를 컨트롤 함

function Comments({ txt, userNum, idNum, setCommentList }) {
    const delClick = e => {
    if (e.target.tagName !== 'BUTTON') {
      return;
    }

    setCommentList(prev => prev.filter(el => el.id !== idNum));
  };
  
return (
  <>
     <li>{txt}</li>
     <button type="button" onClick={delClick}> 삭제 </button>
 </>
   )
}



🙂느낀점

바닐라 자바스크립트로 구현했을 땐, 반복되는 피드 부분을 클래스로 만들어 재사용했다.
그러나 리액트에서는 클래스가 아닌 컴포넌트를 만들어 피드를 찍어냈다.

클래스로 했을땐 돔요소를 직접 지정해서 이벤트를 걸어줬기 때문에 어떤 요소에서 어느 이벤트가 걸려있는지 직관적으로 파악이 어려웠는데,
리액트는 ui렌더 부분과 기능구현 부분이 확연하게 분리되서 한눈에 파악하기 쉬웠다.

6개의 댓글

comment-user-thumbnail
2022년 6월 16일

잘보고 갑니당

1개의 답글
comment-user-thumbnail
2022년 6월 18일

어쩜 이렇게 흡사하게 잘 하셨는지 대단하시네요

1개의 답글
comment-user-thumbnail
2022년 6월 21일

리액트 입문자인데 글도 이해하기 쉽고 너무 예뻐요 ~~! 잘 보고 갑니다 :)

1개의 답글