김마드 2021. 4. 21. 11:41

🍔 핵심 내용

 

🥑 이제 프론트 엔드부분 세팅을 해보자!

원하는 위치에 npx npx create-react-app kimsta-front-web 로 설치 및 깃 연결

 

그리고 필요 파일만 남겨 둔다.

그리고 필수 모듈들을 설치해주자

 

--간단 설명--

styled-components : css

eact-hook-form: 폼 양식을 쉽게 

react-router-dom : 라우터 모듈

react-helmet-async: title을 동적으로

 

npm i styled-components react-hook-form react-router-dom @apollo/client graphql react-helmet-async

 

--폰트, 이미지 관련--

npm i --save @fortawesome/fontawesome-svg-core
npm install --save @fortawesome/free-solid-svg-icons
npm install --save @fortawesome/react-fontawesome
npm install --save @fortawesome/free-brands-svg-icons
npm install --save @fortawesome/free-regular-svg-icons

 

 

**dark mode를 하려면 초기에 미리 세팅 준비를 해두어야 한다. 나중에 다 만들고 다크 모드 만들려면 빡시다고 한다.

 


🍔 핵심 내용

 

🥑 router 기본 개념

 

HashRouter와 Browser Router 2종류가 있는데, deploy 하기에는 HashRouter가 쉽다. Browser는 배포용으로 보면 된다.

 

Hash는 /#/ ~~ 이렇게 진행되는 url이다.

 

🍔 코드 리뷰

 

🥑 App.js

import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

function App() {
  return (
    <div>
      <Router>
        <Switch>
          <Route path="/" exact>
            <h1>홈화면</h1>
          </Route>
          <Route path="/apple">
            <h1>apple</h1>
          </Route>
          <Route path="/banana">
            <h1>banana</h1>
          </Route>
          <Route path="/banana/is">
            <h1>banana is</h1>
          </Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

 특정 /이름 명으로 가게 되면 해당 라우트 안에 있는 데이터가 나오게끔 만드는 개념이다.

 

Switch를 사용하지 않게 되면 예를 들어 /banana/is로 가게 되면 banana와 banana is 가 동시에 화면에 뿌려진다. 

동시애 뿌려짐

어떻게 만드느냐에 따라 위와 같은 요소가 필요할 때도 있지만, 나는 특정 url 주소는 해당 data만 나오게끔 만들 거다. Swtich를 사용하게 되면 이런 곂침 현상(?)이 사라 지게 된다. 1개의 url 주소는 1개만, 그리고 홈 화면에는 exact를 추가해줌으로써 /banana 가 되었던 /apple이 되었던 기본 / 에 영향을 받지 않게 해주었다. 정확히 /(빈 값)이 되어야지만 홈 화면이 나오게 된다.


🍔 핵심 내용

 

🥑 삼항 조건 연산자 활용

삼항 조건 연산자를 활용하여, 로그인 여부에 따라 다른 화면을 보여줄 수 있다.

(추가적으로, 이를 활용하여 로그인 여부와 회원 종류에 따라 각기 다른 화면들을 보여 줄 수 있다.)

 

🥑 텍스트 대신에 스크린 파일을 따로 분리

위에 텍스트대신에 스크린 폴더를 생성하여, 따로 분리해주자. 그리고 없는 url 주소에는 Not Found를 화면에 뿌려주자. 이거 대신에 redirect를 사용 할 수 있지만, 사용자에게 더 혼란을 줄 수 있다. (not found가 더 좋은듯)

 

 

🍔 코드 리뷰

 

🥑 App.js

import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./screens/Home";
import Login from "./screens/Login";
import NotFound from "./screens/NotFound";

function App() {
  const isLoggedIn = false;
  return (
    <Router>
      <Switch>
        <Route path="/" exact>
          {isLoggedIn ? <Home /> : <Login />}
        </Route>
        <Route>
          <NotFound />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;

NotFound는 가장 하단에 위치

 

-- 이하 스크린 폴더 생성--

🥑 screens/Home.js

function Home() {
  return <h1>Home</h1>;
}
export default Home;

🥑 screens/Login.js

function Login() {
  return <h1>please login</h1>;
}
export default Login;

🥑 screens/NotFound.js

function NotFound() {
  return <h1>Not Found</h1>;
}
export default NotFound;

 


🍔 핵심 내용

 

🥑 인증 기능 활성화 하기 개념 이해 1

개념을 먼저 이해하기 위해, props를 활용하여 로그인 로그아웃 기능을 활성화 해보자.

 

🍔 코드 리뷰

 

🥑 App.js

import { useState } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./screens/Home";
import Login from "./screens/Login";
import NotFound from "./screens/NotFound";

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  return (
    <Router>
      <Switch>
        <Route path="/" exact>
          {isLoggedIn ? (
            <Home setIsLoggedIn={setIsLoggedIn} />
          ) : (
            <Login setIsLoggedIn={setIsLoggedIn} />
          )}
        </Route>
        <Route>
          <NotFound />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;

useState를 활용하여, props로 setIsLoggedIn값을 전달해 준다. 초기값은 false이기 때문에 false값이 Login 스크린으로 전달 된다.

 

🥑 Login.js

function Login({ setIsLoggedIn }) {
  return (
    <div>
      <h1>please login</h1>
      <button onClick={() => setIsLoggedIn(true)}>Log in</button>
    </div>
  );
}
export default Login;

 false 값을 받았고, 버튼을 누르면 state값이 true로 변하게 된다. 여기서 state값이 변경 되면, render가 다시 발생 되는데, 이에 setIsLoggedIn 값은 true로 변경되고 해당 true 값은 Home.js 스크린으로 전달 된다.

 

🥑 Home.js

function Home({ setIsLoggedIn }) {
  return (
    <div>
      <h1>Home</h1>
      <button onClick={() => setIsLoggedIn(false)}>Log Out!</button>
    </div>
  );
}
export default Home;

 true 값을 받았고, 버튼을 누르면 false로 바뀌게 된다.

 

위와 같은 로직의 단점은, props가 하위 단계로 전달 되는 것이다. 실제 서비스에서는 이렇게 개발 하면 안된다고 한다. (1단계, 2단계 까진 괜찮아도 점점 하위로 내려가면 안됨)  개념 설명을 위한 내용이고 실제 어떻게 해야 하는지는 다음 내용에 다뤄보자~


🍔 핵심 내용

 

🥑 인증 기능 활성화 하기 개념 이해 2

reactive variable 을 활용하여, 쉽게 구성 할 수 있다.

이는, 백엔드랑 상관 없이 프론트단 안에서 어떤 상태나 값을 바꾸는데 사용 된다. dark mode도 여기에 사용

 

 

🍔 코드 리뷰

 

🥑 apollo.js

import { makeVar } from "@apollo/client";

export const isLoggedInVar = makeVar(false);

reactive variable 선언 (초기값 false)

 

apollo client 는 백엔드 graphql와 상호작용하는데도 쓰이지만, reactive variable 의 역할로도 쓰인다. 

 

🥑 App.js

import { useReactiveVar } from "@apollo/client";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { isLoggedInVar } from "./apollo";
import Home from "./screens/Home";
import Login from "./screens/Login";
import NotFound from "./screens/NotFound";

function App() {
  const isLoggedIn = useReactiveVar(isLoggedInVar);
  return (
    <Router>
      <Switch>
        <Route path="/" exact>
          {isLoggedIn ? <Home /> : <Login />}
        </Route>
        <Route>
          <NotFound />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;

reactive variable 사용

이제 reactive variable 값이 바뀔 때마다 rerender하게 된다.

 

🥑 Login.js

import { isLoggedInVar } from "../apollo";

function Login() {
  return (
    <div>
      <h1>please login</h1>
      <button onClick={() => isLoggedInVar(true)}>Log out!</button>
    </div>
  );
}
export default Login;

버튼을 누르면 reactive variable 값 변경 --> 해당 변경 된 값이 App.js를 rerender하게 만든다.

 

🥑 Home.js

import { isLoggedInVar } from "../apollo";

function Home() {
  return (
    <div>
      <h1>Home</h1>
      <button onClick={() => isLoggedInVar(false)}>Log out!</button>
    </div>
  );
}
export default Home;

isLoggedInVar 값이 true로 변경 및 리렌더 되었고, Home.js 컴포넌트를 실행 하게 된다.

 


🍔 핵심 내용

 

🥑 styled components 활용 

 

styled components를 활용하여, 토글 기능을 통하여 css를 쉽게 변경 할 수 있다. (다크 모드)

 

이 때 활용 되는 개념은, useState와 styled components에도 props전달이 되는 기능이다.

 

**앞서 사용 되었던 reactive variable 는 전역적으로 값이 변경 될때 쓰이면 될 것 같고, useState는 한 컴포넌트 안에서만 사용할 때 쓰면 될 것 같다!

 

일단 코드를 확인해보자.

 

🍔 코드 리뷰

 

🥑 Login.js

import { useState } from "react";
import styled, { css } from "styled-components";

const Container = styled.div`
  background-color: tomato;
`;

const Title = styled.h1`
  color: ${(props) => (props.potato ? "blue" : "orange")};
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
  ${(props) =>
    props.potato
      ? css`
          font-size: 52px;
        `
      : css`
          font-size: 30px;
          background-color: white;
        `}
`;

const ToggleBtn = styled.button`
  color: red;
`;

function Login() {
  const [potato, setPotato] = useState(false);
  const togglePotato = () => setPotato((current) => !current);
  return (
    <Container>
      <Title potato={potato}>please login</Title>
      <ToggleBtn onClick={togglePotato}>Log out!</ToggleBtn>
    </Container>
  );
}
export default Login;

useState를 활용하여 potato라는 상태값을 만들었고, togglePotato를 통해 토글 기능을 만들었다.

 

ToggleBtn 버튼을 누르면 togglePotato 함수가 발동 되고, 버튼을 누를 때마다 potato 값이 true, false -> 토글기능이 발동 된다. 이를 활용하여 css 값을 변경 해줄 수 있는데, styled components는 props 값을 받을 수가 있다. 따라서 potato값이 ture면 A라는 css가 활성화 되고, false면 B라는 css가 활성화 되게끔 만들어 줄 수 있다. 자세한 방법은 코드를 보면 알 수 있다. 여기서 특이점은, css기능 일부만 바꿀 수있고, 아예 css 블록 전체를 다 바꿀수도 있다. 짱짱!

 


🍔 핵심 내용

 

🥑 ThemeProvider의 이해

이를 활용하여 다크모드를 활성화 시킬 수 있다.

 

🍔 코드 리뷰

 

🥑 apollo.js

import { makeVar } from "@apollo/client";

export const isLoggedInVar = makeVar(false);
export const darkModeVar = makeVar(false); // 추가

 

🥑 App.js

import { useReactiveVar } from "@apollo/client";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { ThemeProvider } from "styled-components";
import { darkModeVar, isLoggedInVar } from "./apollo";
import Home from "./screens/Home";
import Login from "./screens/Login";
import NotFound from "./screens/NotFound";

const lightTheme = {
  fontColor: "#2c2c2c",
  bgColor: "lightgray",
};

const darkTheme = {
  fontColor: "lightgray",
  bgColor: "#2c2c2c",
};

function App() {
  const isLoggedIn = useReactiveVar(isLoggedInVar);
  const darkMode = useReactiveVar(darkModeVar);
  return (
    <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
      <Router>
        <Switch>
          <Route path="/" exact>
            {isLoggedIn ? <Home /> : <Login />}
          </Route>
          <Route>
            <NotFound />
          </Route>
        </Switch>
      </Router>
    </ThemeProvider>
  );
}

export default App;

ThemeProvider는 App.js 가장 밖에서 사용 된다. (모든 곳에 css props값이 전달 되어야 하기 때문)

 

reactive variable에 선언한 darkMode값에 따라서, theme가 정해진다. true면 darkTheme이, false면 lightTheme의 객체 값이 props로 각 컴포넌트에 전달 된다.

 

 

🥑 Login.js

import styled from "styled-components";
import { darkModeVar } from "../apollo";

const Container = styled.div`
  background-color: ${(props) => props.theme.bgColor};
`;

const Title = styled.h1`
  color: ${(props) => props.theme.fontColor};
`;

function Login() {
  return (
    <Container>
      <Title>please login</Title>
      <button onClick={() => darkModeVar(true)}>다크모드</button>
      <button onClick={() => darkModeVar(false)}>밝은모드</button>
    </Container>
  );
}
export default Login;

전달 받은, props.theme 값을 styled components에 넣을 수 있고, 버튼기능을 활용하여 다시 reactive variable 값을 바꿔 줄 수 있다. 작동은 문제 없으나, 남은 문제는, 사이트 재접속시 초기값으로 돌아간다는 것이다. 추후에 local storage를 활용하여 데이터를 저장해서 해당 문제를 해결해 보자.

 


🍔 핵심 내용

 

🥑 전체 스타일 적용하는 방법 (body, form 같은 태그에)

import { createGlobalStyle } from "styled-components"; 를 통해 body, form와 같이 전체 태그에 스타일을 적용

할 수 있다.

 

🥑 전체 css 리셋

기본적으로 제공되는 css 요소를 제거해주는 reset를 사용해보자.

npm i styled-reset 설치

 

🍔 코드 리뷰

 

🥑 styles.js

import { createGlobalStyle } from "styled-components";
import reset from "styled-reset";

export const lightTheme = {
  fontColor: "#2c2c2c",
  bgColor: "lightgray",
};

export const darkTheme = {
  fontColor: "lightgray",
  bgColor: "#2c2c2c",
};

export const GlobalStyles = createGlobalStyle`
    ${reset}
    body {
        background-color: ${(props) => props.theme.bgColor};
    }
`;

style 파일을 별도로 만들어 주었다. createGlobalStyle는 theme props를 받는다. (아래 App.js 내용 참고)

그리고, reset를 위 내용과 같이 적용하면 모든 css가 초기화 되고, 내가 설정한 css만 적용이 된다.

 

🥑 App.js

import { useReactiveVar } from "@apollo/client";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { ThemeProvider } from "styled-components";
import { darkModeVar, isLoggedInVar } from "./apollo";
import Home from "./screens/Home";
import Login from "./screens/Login";
import NotFound from "./screens/NotFound";
import { darkTheme, GlobalStyles, lightTheme } from "./styles";

function App() {
  const isLoggedIn = useReactiveVar(isLoggedInVar);
  const darkMode = useReactiveVar(darkModeVar);
  return (
    <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
      <GlobalStyles />
      <Router>
        <Switch>
          <Route path="/" exact>
            {isLoggedIn ? <Home /> : <Login />}
          </Route>
          <Route>
            <NotFound />
          </Route>
        </Switch>
      </Router>
    </ThemeProvider>
  );
}

export default App;

GlobalStyles는 ThemeProvider 하위에 있기 때문에, theme props 영향을 받는다.