코드스테이츠_S2U4_6W_수

윤뿔소·2022년 9월 28일
0

CodeStates

목록 보기
20/47

그 무섭던 리액트를 시작하겠다.

React

  • React의 3가지 특징에 대해서 이해하고, 설명
  • JSX가 왜 명시적인지 이해하고, 바르게 작성
  • React 컴포넌트(React Component)의 필요성에 대해서 이해하고, 설명
  • create-react-app 으로 간단한 개발용 React 앱을 만듦
  • 프론트엔드 개발 JS 오픈 소스 라이브러리!
  • 특징
    1. 선언형 : 'JSX'를 통해 기능을 명시적으로 작성해 코드만 보고 기능을 유추할 수 있고 하나의 파일에서 한 페이지(H,C,J)를 작성 가능!
    2. 컴포넌트 기반 : 하나의 기능 구현을 위해 여러 종류를 묶어놓은 '컴포넌트' 기반으로 개발함! 독립적(유닛 테스트도 용이), 재사용 가능, 유지•보수 용이, 기능 자체에 집중해 개발 가능!
    3. 범용성 : JS가 사용 1등 언어이기도 하면서 JS가 사용된 곳이라면 모두 사용 가능해 범용적! 리액트 네이티브도 있음!@

JSX

이제 기본적인 리액트의 문법에 대해서 알아보겠따!

  • JavaScript XML : JS의 확장된 문법, 문자열도 아니고 HTML도 아님!

    • 그래서 JSX는 JS지만 브라우저가 바로 실행할 수 있는 코드가 아님! 해줄려면 라이브러리 Babel이 필요
    • JSX 코드 컴파일 - Babel이 JS코드 변환 - 브라우저가 그 JS코드를 읽어 렌더링(구현)
  • 리액트에선 UI를 구성할 때 쓰는 문법, 리액트 Element를 만들 수 있음!

    • 바닐라 JS에서의 DOM은 여러 파일을 확인하며 대조해야했음
    • 그래서 JSX로만 마크업 형태의 코드(Element)가 작성 가능하게 만들어 필요한 파일 수도 줄어들고 명시적으로 한눈에 인식 가능해졌음
    • 나중엔 오히려 바닐라 JS의 DOM이 원시적이고 만들기 귀찮다는 인식이 생길 정도 ㅋㅎㅎ
  • 무조건 암기하자

    • 변수 {} 중괄호
    • className으로
    • if문 대신 삼항 연산자
    • 사용자 정의 컴포넌트는 대문자 시작
    • 여러개의 Element 선언 시 감싸주기

문법

JavaScript 문법과 HTML 문법을 동시에 이용해 기능과 구조를 한눈에 확인 : 마크업과 로직이 함께!

import React from "react";

function App() {
  const user = {
    firstName: "React",
    lastName: "JSX Activity"
  };

  function formatName(user) {
    return user.firstName + " " + user.lastName;
  }
  // JSX 없이 활용한 React
  // return React.createElement("h1", null, `Hello, ${formatName(user)}!`);

  // JSX 를 활용한 React with Babel
  return <h1>Hello, {formatName(user)}!</h1>;
}

핵심 : function app()은 위에서 말한 기능과 요소가 함축된 컴포넌트임, JSX가 있고 없고를 보면 훨씬 깔끔하고 가독성도 뛰어나다!

규칙

  1. 2개 이상의 엘리먼트를 추가하려면 하나의 엘리먼트를 감싸는 형태로 쓰고 추가하려는 엘리먼트 작성하기 : opening tag, closing tag 필수
  2. 리액트(JSX)에서 CSS 속성은 class가 아니라 className 필수: class라고 하면 VJS(바닐라)의 클래스로 받아들임
  3. JSX에서 VJS 코드를 쓴다면, 즉! 변수를 써야한다면 중괄호 필수
  4. 리액트 Element가 JSX로 작성되면(사용자 정의 컴포넌트) 대문자 필수 : 소문자로 시작하면 일반적인 HTML로 착각
  5. Element의 조건부 렌더링은 if문이 아니라 삼항연산자로 작성 必(이건 맘에 드네^^)
    • if문 자체는 VJS문장 자체이므로 변수가 안됨(3번)! 그래서 표현식인 삼항 연산자로 써서 변수로 활용 가능이기 때문에 삼항 연산자로 써야함
  6. 여러개의 Element를 넣을려면 고차 함수 map() 必, map()를 사용할 때는 반드시 "key" JSX 속성

예시

const posts = [
  { id: 1, title: "Hello World", content: "Welcome to learning React" },
  { id: 2, title: "Installation", content: "You can install React from npm" }
];
export default function App() {
  // 한 포스트의 정보를 담은 객체 post를 매개변수로 받고
  // obj를 JSX 표현식으로 바꿔 리턴해주는 함수 postToJSX
  const postToJSX = (post) => {
    return (
      <div key={post.id}>
        <h3>{post.title}</h3>
        <p>{post.content}</p>
      </div>
    );
  };
  // postToJSX 함수를 이용하여 여러 개의 엘리먼트롤 표시
  return (
    <div className="App">
      <h1>Hello JSX</h1>
      {posts.map(postToJSX)}
    </div>
  );
}

핵심 : 필수 조건인 map() 활용 및 key: id를 넣어줘 렌더링되게 했음

map()의 활용

'하드 코딩' 같이 노다가성이 띄는 코드들을 전부 작성하는 코드가 있다. 그 코드가 몇개 안된다면 좋겠지만 큰 사이트들은 몇백개에서 몇만개까지 가는 데이터가 우습게 오간다. 그럴 때 일손을 덜어주는 것이 map()의 등장이다.

const posts = [
    { id : 1, title : 'Hello World', content : 'Welcome to learning React!' },
    { id : 2, title : 'Installation', content : 'You can install React via npm.' },
    { id : 3, title : 'reusable component', content : 'render easy with reusable component.' },
    // ...
    { id : 100, title : 'I just got hired!', content : 'OMG!' },
  ];
function Blog() {
  return (
    <div>
      <div>
         <h3>{posts[0].title}</h3>
         <p>{posts[0].content}</p>
     </div>
      <div>
         <h3>{posts[1].title}</h3>
         <p>{posts[1].content}</p>
      </div>
      {// ...}
      <div>
         <h3>{posts[99].title}</h3>
         <p>{posts[99].content}</p>
      </div>
     {// ... 98 * 4 more lines !!}
   </div>
  );
}

위 코드는 하드 코딩을 한 결과다. 사용자 정의 컴포넌트 Blog()를 보면 100개만 하더라도 빡세보이지 않는가? 저걸 더 효율적으로 옮겨보자

// 의사코드 : 열의 각 요소(post)를 특정 논리(postToElement 함수)에 의해 다른 요소로 지정(map)
function Blog() {
  const postToElement = (post) => (
    <div>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  // 코드 가독성을 위해 변수화
  const blogs = posts.map(postToElement);

  return <div className="post-wrapper">{blogs}</div>;
}

짧고, 효율적이고 보기 쉽다. 변수화 말고 return에 써줘도 된다. 하지만 위의 코드는 오류가 뜬다! 왜? key값이 없기 때문

function Blog() {
  // postToElement라는 함수로 나누지 않고 아래와 같이 써도 무방
  const postToElement = posts.map((post) => (
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  ));
  return <div className="post-wrapper">{postToElement}</div>;
}

map()이 함수 표현식 안에 들어감과 동시에 key값이 들어갔다. 효율적!

Component의 필요성

리액트는 컴포넌트 기반 개발 라이브러리다. 컴포넌트에 대해 더 자세히 말하자면 'UI를 구성하는 필수 요소', '리액트의 심장'이라고 할 정도로 중요하다. 그 중요성에 대해 알아보자!

  1. 컴포넌트 : 하나의 기능 구현을 위해 묶어놓은 여러 종류의 코드 모음
  2. 여러 컴포넌트를 묶어 앱 탄생!
  3. 기본적으로 리액트 앱은 최소 1개의 컴포넌트(root)를 가지고 있고, 모든 컴포넌트는 자식 컴포넌트를 가질 수 있다. 즉! 트리 구조를 띈다!
  4. 핵심⭐️ 수정이 용이하다! 예시를 통해 보자

예시 : 유튜브 홈페이지

  1. 기본구조 : Header엔 각각의 컴포넌트인 검색창, 옵션 등이 있고, ContentList에는 동영상인 Content가 있음
  2. 즉! 독립적이면서 트리 구조로 이뤄져 있음, 이 컴포넌트들을 한데 모아 복잡한 UI를 구성하고, 그 UI들을 한데 모아 앱을 만들수 있다!
  3. 수직적인 구조의 페이지를 수평적인 구조로 바꾼다면?이렇게 UI에 맞춰 위치만 수정되니 보수에 용이하다!
    DOM이었음 HTML로 구조 바꾸기 - CSS 수정 - 변경된 구조와 디자인에 맞춰 DOM 조작 해야해서 비교적 귀찮음!

Create React App

이제 리액트를 시작하는데 있어 필요한 준비 동작을 만들어 보겠다!@

  • Create React App 소개를 보고, Create React App 이 무엇인지 대략적으로 이해할 수 있다.
  • npx create-react-app 으로 새로운 리액트 프로젝트를 시작
  • create-react-app 으로 간단한 개발용 React 앱을 실행
    리액트 랜덤 명언 앱 튜토리얼을 따라 간단한 리액트 랜덤 명언 앱을 만듦
  • Create React App으로 만들어진 리액트 프로젝트 구성을 대략적으로 이해

정의 : 리액트 SPA를 만드는데 쉽고 빠르게 개발할 수 있도록 하는 툴체인

만들려는 폴더 들어가서 npx create-react-app 폴더 이름 입력
만들면! react, react-dom, react-scripts 등등이 깔리며 'Happy Hacking'이란 메시지가 나오면 만들기 끝! npm run start로 실행해보자

구조

  • node의 module들이 깔렸음
  • public으로 앱을 만들면 공통적으로 생성되는 요소 생성
  • 우리가 만질 건 src 폴더에 코드를 입력한다!
    • index.js에 기본적으로 깔린 모듈을 불러오는 import 키워드와 render 메소드가 있어 렌더링을 해준다.
    • App.js의 App()에 아까 배웠던 JSX와 기본 코드들을 작성하면 됨!
      예시로 명언 랜덤 제조 앱을 만들어보자

예시

새로고침으로 만드는 간단한 명언 랜덤 제조
참고 : '핫리로딩'이라는 기능이 기본적으로 적용돼있어 바로바로 새로고침됨

// index.js
<App />,
document.getElementById('root')
// app.js
function App() {
  // 배열 데이터 생성
  const proverbs = [
    "인생은 재미다.",
    "메모를 하는 건 기억하기 위함이 아니라 잊기 위함이다.",
    "우리가 배우는 의미는 그 자체로 익숙해지기 위해서다.",
    "어린 아이에게 설명해주지 못하는 이론은 이해하지 못한 이론이다.",
    "침묵도 하나의 언어다.",
    "아무리 가까운 사이라도 어느정도 거리가 있어야한다.",
    "사회에서 우선적으로 공감 후 해결책을 제시하는 것이 마땅하다.",
    "인간 관계를 확장하고 유지하는 방법은 상대에게 내가 중요한 사람이 되었다는 인식을 심어주는 것이다.",
  ];
  // 랜덤함수 제작
  const getRandomIdx = (length) => {
    return parseInt(Math.random() * length);
  };
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          <code>src/App.js</code> 새로고침하면 새로운 명언을 볼 수 있습니다.
        </p>
		// 랜덤함수 사용
        {proverbs[getRandomIdx(proverbs.length)]}
      </header>
    </div>
  );
}

과제

트위터와 비슷한 'Twittler'를 리액트로 코딩해보자!

기본구조

├── /React Twittler State Props
│   ├── README.md
│   ├── /public               # create-react-app이 만들어낸 파일로 yarn/npm start로 실행 시에 쓰임
│   └── /src                  # React 컴포넌트가 들어가는 폴더
│        ├── static           # dummyData가 있는 폴더
│        │    └── dummyData.js
│        ├── App.css
│        ├── App.js           # Twittler App이 작성
│        ├── index.js
├  package.json
└ .gitignore
  1. 기본적으로 index.js랑 App.js가 시험에 맞게 적용돼있다.
  2. 더미 데이터를 연결시켜야한다!
  3. 연결시킨 태그들을 가져와서 컴포넌트 형식으로 넣어야한다!

수행

const Sidebar = () => {
  // 어썸 폰트 아이콘 가져오기
  return <section className="sidebar">{<i className="far fa-comment-dots"></i>}</section>;
};

const Counter = () => {
  return (
    <div className="tweetForm__input">
      <div className="tweetForm__inputWrapper">
        <div className="tweetForm__count" role="status">
          {/* dummyTweet로 전달되는 데이터의 갯수 */}
          {"Total : " + dummyTweets.length}
        </div>
      </div>
    </div>
  );
};

const Footer = () => {
  return (
    <div>
      {<footer></footer>}
      <img id="logo" src={`${process.env.PUBLIC_URL}/codestates-logo.png`} />
      Copyright @ 2022 Code States
    </div>
  );
};
// TODO : Footer 함수 컴포넌트를 작성, 시멘틱 엘리먼트 footer가 포함

const Tweets = () => {
  return (
    <ul className="tweets">
      {dummyTweets.map((tweet) => {
        const isParkHacker = tweet.username === "parkhacker" ? "tweet__username tweet__username--purple" : "tweet__username";
        return (
          <li className="tweet" key={tweet.id}>
            <div className="tweet__profile">{<img src={tweet.picture}></img>}</div>
            <div className="tweet__content">
              <div className="tweet__userInfo">
                {/* {<span className="tweet__username">{tweet.username}</span>} */}
                {/* {tweet.username === "parkhacker" ? (
                  <span className="tweet__username tweet__username--purple">{tweet.username}</span>
                ) : (
                  <span className="tweet__username">{tweet.username}</span>
                )} */}
                <span className={isParkHacker}>{tweet.username}</span>
                {<span className="tweet__createdAt">{tweet.createdAt}</span>}
              </div>
              {/* 트윗 메세지 */}
              <div className="tweet__message">{tweet.content}</div>
            </div>
          </li>
        );
      })}
    </ul>
  );
};

const Features = () => {
  return (
    <section className="features">
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile"></div>
          <Counter />
        </div>
      </div>
      <Tweets />
      <Footer />
    </section>
  );
};

const App = () => {
  return (
    <div className="App">
      <main>
        <Sidebar />  {/* ={Sidebar()} */}
        <Features />
      </main>
    </div>
  );
};

// ! 아래 코드는 수정하지 않습니다.
export { App, Sidebar, Counter, Tweets, Features, Footer };

알게된 것

  • 더미데이터의 객체를 가져온다면, 즉! 변수로 가져오면 {}는 필수구나!
  • App, Sidebar, Counter, Tweets, Features, Footer가 트리 구조로 연결돼있다!
  • map()은 진짜 key가 중요하구나! key는 중복된 데이터들을 걸러주면서 적용하기 때문에 map()에게 중요한 속성이구나!
    1. obj.id
    2. 배열이면 index(권장 x: 인덱스가 변할 수도 있어서)
    3. 공식 사이트 설명 : key는 반드시 변하지 않고, 예상 가능하며, 유일한 값 必 : 변하는 key(Math.random()으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실됨
  • 함수 스코프? 함수의 적용 범위를 다시 복습하자.. 틀렸음
    • 위에 {Sidebar()}랑 비슷한 의미
    • 함수 표현식으로 어떻게 정의돼있는지 확인하자
let langs = ["JavaScript", "HTML", "Python"];
  let viewLangs = () =>  {
    return langs.map((it) => { //함수에 감싸져있음, viewLangs가 실행돼야 map이 실행되는 구조
      return <p>{it}</p>;
    });
  };

  return (
    <div>
      {viewLangs()} // 함수 그 자체이므로 실행을 넣어줌
    </div>
  );
let langs = ["JavaScript", "HTML", "Python"];
  let viewLangs = langs.map((it) => { // 함수 호출 결과가 변수에 바로 할당됨
      return <p>{it}</p>;
    });
    
  return (
    <div>
      {viewLangs}
    </div>
  );
  • 사용자 정의 컴포넌트를 삽입할 때 {()}는 함수 자체 값을 리턴하는 느낌이고 </>는 react 문법들을 사용할 수 있는 요소를 리턴하는 느낌
profile
코뿔소처럼 저돌적으로

0개의 댓글