Dnote 4 - 2. React - 페이지 구조 생성.

killi8n·2018년 9월 21일
6

Dnote

목록 보기
7/15
post-thumbnail

지난 튜토리얼에서 만든 API를 바탕으로 통신하여, 리액트에서 CRUD를 실시해보겠습니다.

일단 처음에 react-router-dom을 사용하여 클라이언트쪽 라우팅을 해보겠습니다.

$ yarn add react-router-dom

Root.js

import React from "react";
import App from "components/App";
import { Provider } from "react-redux";
import store from "store/configure";
import { BrowserRouter } from "react-router-dom";

const Root = () => {
  return (
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  );
};

export default Root;

Root.js를 다음과 같이 BrowserRouter로 감싸줍니다.

그런 뒤 라우팅에 쓰이게 될 pages들을 생성하겠습니다.

pages밑에 다음과 같은 파일들을 생성해주세요.

Main.js: 노트들이 리스트되어 나타날 페이지

import React from "react";

const Main = () => {
  return <div>Main Page</div>;
};

export default Main;

Auth.js: 로그인 / 회원가입 페이지

import React from "react";

const Auth = () => {
  return <div>Auth Page</div>;
};

export default Auth;

NotFound.js: 라우팅 되지 않은 곳에서 나타날 페이지

import React from "react";

const NotFound = () => {
  return <div>NotFound Page</div>;
};

export default NotFound;

그런뒤 이 페이지들을 한곳에 모아줄 index.js를 생성합니다.

pages/index.js

export { default as Main } from "./Main";
export { default as Auth } from "./Auth";
export { default as NotFound } from "./NotFound";

App.js에서 페이지들을 라우팅 해줍니다.

App.js

import React, { Component } from "react";
import { Switch, Route } from "react-router-dom";
import { Main, Auth, NotFound } from "pages";

class App extends Component {
  render() {
    return (
      <div>
        <Switch>
          <Route path="/" exact={true} component={Main} />
          <Route path="/auth/:kind" exact={true} component={Auth} />
          <Route component={NotFound} />
        </Switch>
      </div>
    );
  }
}

export default App;

라우팅이 끝났으면, 이제 다음과 같은 주소들이 작동할것입니다.

http://localhost:3000/ -> Main
http://localhost:3000/auth/login -> Auth
http://localhost:3000/auth/register -> Auth
http://localhost:3000/alskjdalskdj -> NotFound

그럼 이제 라우팅은 되었으니, 실제 UI들을 조금씩 만들어보겠습니다.

메인 스트럭쳐를 뜻하는 structure 폴더를 components 밑에 생성해줍니다.

그 뒤 Header를 만들어주겠습니다.

다음과 같은 구조를 가집니다!
아 그리고 로그아웃 버튼을 만들기 위해 react-icons를 인스톨 해줍니다.
또한, color제공 라이브러리인 open-color와 미디어 쿼리를 좀더 자유롭게 사용하게 해주는 include-media도 다운로드 해주겠습니다.
classnames는 여러개의 클래스를 한번에 사용하게도 하고, scss와 bind하여 굳이 styles라는 이름을 사용하지 않아도 되게하는 라이브러리 입니다.

$ yarn add react-icons open-color include-media classnames

그리고 이 라이브러리들을 모든 scss에서 적용할수 있게 해보겠습니다.

styles/utils.scss

@import "~open-color/open-color";
@import "~include-media/dist/include-media";

$breakpoints: (
  small: 320px,
  medium: 768px,
  large: 1024px,
  wide: 1400px
);

앞으로 모든 scss파일에서 임포트 하게될 utils.scss를 생성했습니다.

Header.js

import React from "react";
import styles from "./Header.scss";
import classNames from "classnames/bind";
import { Link } from "react-router-dom";
import { MdLock } from "react-icons/md";

const cx = classNames.bind(styles);

const Header = ({ onLogout }) => (
  <div className={cx("header")}>
    <Link to={"/"} className={cx("logo")}>
      D-Note
    </Link>
    <div className={cx("logout")}>
      <MdLock onClick={onLogout} />
    </div>
  </div>
);

export default Header;

Header.scss

@import "utils";

.header {
  background: $oc-violet-6;
  height: 5rem;

  display: flex;
  align-items: center;

  .logo {
    color: white;

    font-weight: 800;
    font-size: 1.5rem;

    cursor: pointer;

    margin-left: 1rem;

    user-select: none;
  }

  .logout {
    margin-left: auto;
    margin-right: 1rem;
    color: white;

    font-weight: 800;
    font-size: 2rem;

    cursor: pointer;
  }
}

index.js

export { default } from './Header';

그럼 이제 이 헤더를 반영시켜볼까요?

pages/Main.js

import React from "react";
import Header from "components/structure/Header";

const Main = () => {
  return <Header />;
};

export default Main;

잘 나타나나요?

근데 이런식으로 매 페이지에서 Header를 import 하는건 비효율 적이므로, 페이지 구조를 만들어주는 컴포넌트를 하나 생성하겠습니다.

components/structure/MainStructure.js

import React from "react";
import styles from "./MainStructure.scss";
import classNames from "classnames/bind";
import Header from "components/structure/Header";

const cx = classNames.bind(styles);

const MainStructure = ({ children }) => (
  <div>
    <Header />
    <main>{children}</main>
  </div>
);

export default MainStructure;

components/structure/index.js

export { default } from './MainStructure';

그리고 이 MainStructure를 Main 페이지에 반영해줍니다.

pages/Main.js

import React from "react";
import MainStructure from "components/structure/MainStructure";

const Main = () => {
  return <MainStructure>Main Page</MainStructure>;
};

export default Main;

다음에는 노트를 입력하는 노트 입력 폼을 만들어보겠습니다.

profile
killi8n

6개의 댓글

comment-user-thumbnail
2019년 1월 23일

포스팅 정말 잘 보고 있습니다! 감사해요~

하나 궁금한게 있는데요. 지금 이 포스팅에서 사용되는 상대경로 값에 대한 질문입니다.
js 파일의 경로는 .env 파일에 의해서 src가 최상단으로 지정되는 것은 알겠는데
scss 파일의 import 부분에서의 경로는 yarn eject를 해서 따로 설정해주어야 가능한게 아닌가 싶습니다.
eject 이후 설정값이 변경되었다는 전제하에 진행 중인건가요?

추가적으로, 좋은 정보가 있어서 공유해요.
CRA 2.0 에서 추가된 CSS 모듈에 대한 정보에요.
모듈 추가 없이 CRA에서 제공하는 기능으로 className 사용할 수 있네요.
https://youtu.be/w9Zf0hpohQM?t=256

1개의 답글
comment-user-thumbnail
2019년 12월 9일
  1. import styles from "./MainStructure.scss";
    위의 scss 파일은 어떤내용인지 안나와있네요. 혹시 Header.scss를 잘못 기입하신건가요?

  2. components/structure/index.js
    처음에는 index.js 는 components/structure/Header 폴더 안에 생성하라고 하셨는데 중간에 index.js의 경로는 위와 같이 수정되어있네요 처음이 맞는건가요?

  3. @import "utils";
    Header.scss 에서 위와 같이 import 했을 시 not found 에러가 발생하는데 어떤 연유인지 모르겠습니다.
    해당파일의 경로를 직접 입력하면 되는데 위와같이 했을 경우 에러가 발생하네요.

1개의 답글