Skip to content
egoing edited this page Feb 16, 2021 · 22 revisions

step0

목표

리액트의 개발환경을 구축하고 싶다.

힌트

  1. nodejs 설치
  2. npm i create-react-app
  3. create-react-app myapp
  4. cd myapp
  5. npm start

결과

step1

목표

아래의 코드를 리액트화하고 싶다.

<header>
        <h1>WEB</h1>
      </header>
      <nav>
        <ul>
          <li>
            <a href="/1">html</a>
          </li>
          <li>
            <a href="/2">css</a>
          </li>
          <li>
            <a href="/3">javascript</a>
          </li>
        </ul>
      </nav>
      <article>
        <h2>Welcome</h2>
        Hello, WEB!
      </article>

결과

step2

목표

header, nav, article 태그가 너무 복잡합니다. 이들을 컴포넌트화하고 싶다.

힌트

각 태그를 Header, Nav, Article 라는 이름의 컴포넌트로 만들어서 단순화 해보세요.

function Header() {
  //todo
}
function Nav() {
  //todo
}
function Article() {
  //todo
}
export default function App() {
  return (
    <div>
      <Header />
      <Nav />
      <Article />
    </div>
  );
}

결과

step 3

목표

컴포넌트가 너무 경직 되어 있습니다. HTML 태그 처럼 속성을 통해서 컴포넌트를 커스터마이징 하고 싶습니다.

힌트

아래와 같은 인터페이스를 가지고 있는 컴포넌트를 만들어보세요.

function Nav(props) {
  //todo
}
function Article(props) {
  //todo
}
export default function App() {
  return (
    <div>
      <Header />
      <Nav
        data={[
          { id: 1, title: "html", description: "html is..." },
          { id: 2, title: "css", description: "css is..." },
          { id: 3, title: "js", description: "js is..." }
        ]}
      />
      <Article title="제목" description="본문" />
    </div>
  );
}

결과

step 4

목표

컴포넌트에 이벤트 인터페이스를 추가해서 핸들러의 상태 변화를 통보 받을 수 있도록 하고 싶다.

힌트

function Header(props) {
  //todo
}
function Nav(props) {
  //todo
}
export default function App() {
  var article = null;
  if (mode === "WELCOME") {
    // todo
  } else if (mode === "READ") {
    // todo
  }
  return (
    <div>
      <Header
        onChangeMode={function() {
          setMode("WELCOME");
        }}
      />
      <Nav
        data={topics}
        onChangeMode={function(topic_id) {
          setMode("READ");
          setSelectedId(topic_id);
        }}
      />
      {article}
    </div>
  );
}

결과

step 5

목표

글생성 기능을 추가하고 싶습니다.

알아야 할 것

  1. 데이터를 변경할 때는 불변성을 유지하도록 주의 합니다.
    1. 원시 데이터 타입은 그냥 변경하면 됩니다.
    2. 배열과 객체는 원본을 복제한 후에 변경하면 됩니다.
    3. 차이는 다음 영상을 참고해주세요. https://youtu.be/fDJG4CjbB3A?t=215 참고 : https://gist.github.com/aluksidadi/10aa07c6f007efc587f793212548ad51
  2. immutable 과 관련된 이슈는 다음 영상을 참고하세요. https://opentutorials.org/module/4075
  3. immutable.js 와 같은 라이브러리를 이용하는 것을 추천합니다.
  4. 기존 js 문법의 문제점과 immutable.js를 사용하는 이유 : https://docs.google.com/spreadsheets/d/1tn5YI9wDZieZOwJHCaT_cQuixw64tr_iv3Pv6AqdfI8/edit#gid=0

힌트

function Create(props) {
  return (
    <article>
      <h1>Create</h1>
      <form
        action="topics"
        method="post"
        onSubmit={function(e) {
          //todo
        }}
      >
        <p>
          <input type="text" name="title" placeholder="title" />
        </p>
        <p>
          <textarea name="description" placeholder="description" />
        </p>
        <p>
          <input type="submit" />
        </p>
      </form>
    </article>
  );
}
function Control(props) {
  return ( 
    <ul>
      <li>
        <input
          type="button"
          value="create"
          onClick={function() {
            //todo
          }}
        />
      </li>
    </ul>
  );
}
export default function App() {
  var article = null;
  if (mode === "WELCOME") {
  } else if (mode === "READ") {
  } else if (mode === "CREATE") {
    article = (
      <Create
        onCreate={function(data) {
          data.id = nextId;
          setTopics([...topics, data]);
          setSelectedId(nextId);
          setMode("READ");
          setNextId(nextId + 1);
        }}
      />
    );
  }
  return (
    <div>
      //...
      <Control
        onChangeMode={function(mode) {
          setMode(mode);
        }}
      />
    </div>
  );
}

결과

step 6

목표

수정 기능을 추가하고 싶습니다.

알아야 할 것

  • 폼을 수정할 때는 onChange 이벤트를 통해서 state 값을 수정하고, 수정된 state값이 value에 반영될 수 있도록 처리해야 합니다.

힌트

function Update(props) {
  var [title, setTitle] = useState(props.data.title);
  var [description, setDescription] = useState(props.data.description);
  return (
    <article>
      <h1>Update</h1>
      <form
        action="topics"
        method="put"
        onSubmit={function(e) {
          //todo
        }}
      >
        <p>
          <input
            type="text"
            name="title"
            placeholder="title"
            value={title}
            onChange={function(e) {
              //todo 
            }}
          />
        </p>
        <p>
          <textarea
            name="description"
            placeholder="description"
            value={description}
            onChange={function(e) {
              //todo
            }}
          />
        </p>
        <p>
          <input type="submit" />
        </p>
      </form>
    </article>
  );
}
function Control(props) {
  //todo
}
export default function App() {
  //todo
  } else if (mode === "UPDATE") {
    var selectedTopic = null;
    for (var i = 0; i < topics.length; i++) {
      if (topics[i].id === selectedId) {
        selectedTopic = topics[i];
        break;
      }
    }
    article = (
      <Update
        data={selectedTopic}
        onUpdate={function(data) {
          var newTopics = [...topics];
          for (var i = 0; i < newTopics.length; i++) {
            if (newTopics[i].id === data.id) {
              newTopics[i].title = data.title;
              newTopics[i].description = data.description;
              break;
            }
          }
          setTopics(newTopics);
          setMode("READ");
        }}
      />
    );
  }
  //...
}

결과

step 7

목표

삭제를 구현하고 싶습니다.

힌트

<Control
    onChangeMode={function(mode) {
      if (mode === "DELETE") {
        todo 
      }
    }}
/>

결과

step 8

목표

코드가 너무 많아졌습니다. 코드를 파일로 분리해서 정리 정돈하고 싶습니다.

알아야 할 것

export, import
https://stackblitz.com/edit/react-exim?file=src/App.js
MyModule.js

// 외부에 노출을 원하지 않는 함수
function privateFunc() {
  return "private";
}
// privateFunc를 이용해서 외부에서 사용되도록 고안된 함수
function publicFunc() {
  return privateFunc() + " + " + "public";
}
// 외부에서 사용할 수 있도록 export 한다.
export { publicFunc };
// 외부에서 renamedFunc로 사용하도록 한다.
export { publicFunc as renamedFunc };
// var, const, let도 가능하다.
export var publicVar = "public var";
function defaultFunc() {
  return "default";
}
export default defaultFunc;

App.js

import { publicFunc } from "./MyModule";
console.log(publicFunc());
import { renamedFunc } from "./MyModule";
console.log(renamedFunc());
import { publicVar } from "./MyModule";
console.log(publicVar);
import my from "./MyModule";
console.log(my());

결과

step 9 - ajax

이번 임무는 step8을 선행학습으로 하고 있습니다

목표

ajax를 이용해서 리로드 없이 동적으로 데이터를 변경하고 싶습니다.

알아야 할 것

useEffect - 사이드 이팩트란 무엇인가? react 에서 ajax 통신하는 방법 .env.development.template => .env.development 로 변경해서 서버와 통신 할 REACT_APP_API_URL 값을 셋팅해줘야 한다.

결과

step 9 - route

이번 임무는 step8을 선행학습으로 하고 있습니다

목표

UI에 따라서 페이지 리로딩 없이 URL이 변경되고, URL에 따라서 적당한 정보가 표시되도록 개선하고 싶습니다.

알아야 할 것

  • history.pushState를 이용해서 URL을 변경하는 방법

결과

step 10 - route

목표

step 9 - route 에서 구현한 내용을 react-route-dom을 이용해서 개선하고 싶습니다.

알아야할 것

  • react-route-dom을 이용하는 방법
  • react-route-dom을 이용해서 if 문을 없애고, 보다 단순하게 코드를 제어할 수 있게 됨을 공감

참고

결과

Clone this wiki locally