Gom3rye

Who Am I] MBTI 테스트 만들기 본문

웹 개발

Who Am I] MBTI 테스트 만들기

Gom3rye 2022. 10. 7. 14:03

부트스트랩 사용 방법

npm install react-bootstrap bootstrap

터미널에 위 명령어 입력 후

<script src="https://unpkg.com/react/umd/react.production.min.js" crossorigin></script>

<script
  src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
  crossorigin></script>

<script
  src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
  crossorigin></script>

<script>var Alert = ReactBootstrap.Alert;</script>

위 코드를 public > index.html 상단에 넣어준다. (<head> 아래)

import 'bootstrap/dist/css/bootstrap.min.css';

그 후 index.js 에 위 코드를 넣어준다.

<출처 :https://react-bootstrap.github.io/getting-started/introduction>

적용

 Border를 넣어주고 싶다!

-> <img src={PangImage} className="rounded-circle"> 처럼 className을 추가해준다. (rounded-circle을 넣을지 다른 것을 넣을지는 https://getbootstrap.com/docs/5.2/utilities/borders/#border 여기서 찾아주면 된다.)

부트스트랩을 이용해서 버튼 넣어주기

import Button from 'react-bootstrap/Button';

버튼을 넣고 싶은 js 파일에 위 코드를 넣어준다.

페이지 라우팅 하기

먼저 라우터를 다운받아야 한다.

npm i react-router-dom
import Home from './pages/Home';
import Question from './pages/Question';
import Result from './pages/Result';
import { Routes, Route } from 'react-router-dom';
import './App.css';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/question" element={<Question />} />
      <Route path="/result" element={<Result />} />
    </Routes>
  );
}

export default App;

위 처럼 Routes와 Route를 이용해 원하는 페이지 라우팅을 진행한다.

But, 이렇게만 하면 라우팅이 되지 않는다.

index.js 파일에 가서

import React from 'react';
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
);

지금 사용하고 있는 App.js 파일을 <BrowerRouter>로 감싸줘야지 라우팅이 된다.

라우팅이 잘 되었는지 확인하는 방법

라우트 path로 지정한 url을 쳐보고 해당 화면이 뜨면 라우팅이 잘 된 것이다. Ex) http://localhost:3000/question

무료 폰트 적용

1. 눈누에서 원하는 폰트를 다운받는다.

2. src > assets > fonts 폴더를 만들어 다운받은 폰트들을 넣는다.

3. src > App.css 파일을 만들어(처음에 지웠으니 다시 만들어준다.) 

.App {
  font-family: 'DalseoHealingBold';
}

@font-face {
  font-family: 'DalseoHealingBold';
  src: url(/src/assets/fonts/DalseoHealingBold.ttf);
}

위와 같이 작성해준다. (font-famil : ' fonts 폴더에 있는 이름 그대로 ')

4. App.js 파일로 와서

import './App.css';

위 코드를 넣어준다.

5. 폰트를 넣고 싶은 파일로 가서 styled-componets 내용 안에 font-family: 'DalseoHealingBold'; 를 넣어준다.

Ex)

const Header = styled.div`
  font-size: 40pt;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: 'DalseoHealingBold';
`;

(부트스트랩으로 만든 버튼은 font가 적용되지 않으니

<Button style={{ fontFamily: 'DalseoHealingBold' }}
    onClick={handleClickButton}> 테스트 시작하기 </Button>

위 처럼 inline styling을 해주면 된다. 하지만 굳이 이렇게 하지 않아도 부트스트랩 버튼에도 스타일링이 적용되었긴 하다. 만약 안 되었을 시 위 방법을 사용하면 되겠다.)

버튼 클릭시 이동

버튼 태그에 onClick 이벤트를 넣어주면 된다.

함수 이름은 handleClickButton으로 정했고 해당 js 파일에

const handleClickButton = () => {
    // 이전에는 useHistory라는 Hooks를 써서 진행했지만 최근에는 react router dom이 업그레이드 하면서 useNavigate 사용
    navigate('/question');
    // 그냥 해도 되지만 navigate라고 함수를 썼으니 이 const 위에 const navigate = useNavigate();도 선언해주는 것을 잊지 말자.
  };

위 코드를 return문 위에 선언해준다. 물론 useNavigate를 사용하기 위해서

import { useNavigate } from 'react-router-dom';

위 코드를 넣어주는 것도 잊지 말자.

 

완성된 Home.js 파일

import React from 'react';
//css-in-js
import styled from 'styled-components';
import PangImage from '../assets/푸루립밤2.JPG';
import Button from 'react-bootstrap/Button';
import { useNavigate } from 'react-router-dom';

const Home = () => {
  const navigate = useNavigate();

  const handleClickButton = () => {
    // 이전에는 useHistory라는 Hooks를 써서 진행했지만 최근에는 react router dom이 업그레이드 하면서 useNavigate 사용
    navigate('/question');
  };

  return (
    <Wrapper>
      <Header>비건 화장품 판결기</Header>
      <Contents>
        <Title>나에게 맞는 화장품은?</Title>
        <LogoImage>
          <img
            src={PangImage}
            className="round-circle"
            width={350}
            height={350}
          />
        </LogoImage>
        <Desc>MBTI를 기반으로 하는 나랑 잘맞는 화장품 찾기!</Desc>
        <Button
          style={{ fontFamily: 'DalseoHealingBold' }}
          onClick={handleClickButton}
        >
          테스트 시작하기
        </Button>
      </Contents>
    </Wrapper>
  );
};

export default Home;

const Wrapper = styled.div`
  height: 100vh;
  width: 100%;
`;

const Header = styled.div`
  font-size: 40pt;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: 'DalseoHealingBold';
`;

const Contents = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  font-family: 'DalseoHealingBold';
`;

const Title = styled.div`
  font-size: 30pt;
  margin-top: 40px;
`;

const LogoImage = styled.div`
  margin-top: 10px;
`;

const Desc = styled.div`
  font-size: 20pt;
  margin-top: 20px;
`;

부트스트랩 프로그래스 바 넣어주기

1. https://react-bootstrap.netlify.app/components/progress/#rb-docs-content 에서 progress bar 검색 후 마음에 드는 바 선택

2. 아래 코드를 넣은 후

import { ProgressBar } from 'react-bootstrap';
<ProgressBar striped variant="danger" now={80} />

위 코드를 넣어주면 끝!

CSS 측면에서 고려한 점

선지가 길어지면 양쪽의 답변 높이가 달라질 수 있기 때문에 최소 높이를 지정해준다.

Ex)

<Button style={{
            width: '40%',
            minHeight: '200px',
            fontSize: '15pt',
            marginLeft: '20px',
          }}
> 나는 답변 2
</Button>

데이터 뿌려주는 방법

1. src > assets > data > questiondata.js 파일을 만든다.

Ex)

// 컴포넌트가 아닌 단순히 객체 배열을 생성할 것이기 때문에 export만 해주면 된다.
export const QuestionData = [
  {
    id: 1,
    title: '문제1',
    answera: '답변1',
    answerb: '답변2',
    type: 'EI',
  },
];

2. 뿌려주고 싶은 파일에 가서

import { QuestionData } from '../assets/data/questiondata';

import 해준 후

<Title>{QuestionData[0].title}</Title>
      <ButtonGroup>
        <Button style={{ width: '40%', minHeight: '200px', fontSize: '15pt' }}>
          {QuestionData[0].answera}
        </Button>
        <Button
          style={{
            width: '40%',
            minHeight: '200px',
            fontSize: '15pt',
            marginLeft: '20px',
          }}
        >
          {QuestionData[0].answerb}
        </Button>
      </ButtonGroup>

위 코드처럼 {}를 사용해 뿌려주면 된다.

onClick 이벤트

- 변수를 안 넘겨 주었을 때

onClick={handleClickButton}

- 변수를 넘겨 주었을 때

onClick={()=>handleClickButtonA(1)}

Question Number 테스트 로직 만들기

버튼을 누르면 question 넘버에 +1이 되면서 다음 문제로 넘어가게 만들어야 한다.

const [questionNo, setQuestionNo] = React.useState(0);
const handleClickButtonA = (no) => {  //여기서 no는 위 코드블럭에 있는 1이 되는 것
    setQuestionNo(questionNo + 1);
};
const handleClickButtonB = (no) => {  //인자를 받은 이유 : 배점값 표시(2점 이상 받으면 왼쪽 type으로!)
    setQuestionNo(questionNo + 1);
};

그리고 [0] 대신에 questionNo를 넣기

<Title>{QuestionData[questionNo].title}</Title>

완료한 문제만큼 프로그래스 바 움직이게 하기

<ProgressBar
        striped
        variant="danger"
        now={(questionNo / QuestionData.length)*100} // %로 움직이기 때문에 *100 해주기!
        style={{ marginTop: '20px' }}
/>

버튼을 눌렀을 때 totalScore 배열에 값을 추가해주는 로직

새로운 객체를 만들어서 각각의 객체를 totalScore 배열 안에 index를 찾아서 그 객체를 대신해주는 방법으로 로직 작성

Map함수를 이용해 코드 최적화

- 배점을 넘겨 주기 때문에 함수 안에서 왼쪽 버튼인지 오른쪽 버튼인지 구분할 수 있다.

: questionDataType === totalScore의 id 때, 서칭을 통해 같은 것의 score를 하나씩 더해주는 방식으로 코드 구현 가능

 

위에서 새로운 데이터의 값을 계산했다. 이곳에서 totalScore를 이용하게 되면 여기 있는 setTotalScore로 newScore가 바뀌게 되는 시점은 handleClickButton 함수가 끝나고 난 시점이기 때문에 totalScore를 이용하지 않고 newScore를 이용해서 mbti를 도출해주도록 한다.

Reduce 함수

리듀서 함수는 누산기 accumulator(acc), 현재 값 (cur), 현재 인덱스 (idx), 원본 배열 (src)의 네 개의 인자를 가집니다. 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 된다.

 

result 페이지의 경우 새로운 컴포넌트이기 때문에 navigate에서 함께 이 주소와 mbti를 같이 넘겨주도록 하겠다.

카카오톡 공유하기 기능 구현

1. 카카오톡 developers > 문서 > 메시지 > 카카오톡 공유: JavaScript

https://developers.kakao.com/docs/latest/ko/message/js-link 에서 직접 만든 버튼 사용하기 아래에 있는 코드를 복사해서 src > components 폴더를 생성한 후 KaokaoShareButton.js 파일에 붙여넣는다.

import React from 'react';

const KakaoShareButton = () => {
  Kakao.Share.sendDefault({
    objectType: 'feed',
    content: {
      title: '오늘의 디저트',
      description: '아메리카노, 빵, 케익',
      imageUrl:
        'https://mud-kage.kakao.com/dn/NTmhS/btqfEUdFAUf/FjKzkZsnoeE4o19klTOVI1/openlink_640x640s.jpg',
      link: {
        mobileWebUrl: 'https://developers.kakao.com',
        androidExecutionParams: 'test',
      },
    },
    itemContent: {
      profileText: 'Kakao',
      profileImageUrl:
        'https://mud-kage.kakao.com/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png',
      titleImageUrl:
        'https://mud-kage.kakao.com/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png',
      titleImageText: 'Cheese cake',
      titleImageCategory: 'Cake',
      items: [
        {
          item: 'Cake1',
          itemOp: '1000원',
        },
        {
          item: 'Cake2',
          itemOp: '2000원',
        },
        {
          item: 'Cake3',
          itemOp: '3000원',
        },
        {
          item: 'Cake4',
          itemOp: '4000원',
        },
        {
          item: 'Cake5',
          itemOp: '5000원',
        },
      ],
      sum: '총 결제금액',
      sumOp: '15000원',
    },
    social: {
      likeCount: 10,
      commentCount: 20,
      sharedCount: 30,
    },
    buttons: [
      {
        title: '웹으로 이동',
        link: {
          mobileWebUrl: 'https://developers.kakao.com',
        },
      },
      {
        title: '앱으로 이동',
        link: {
          mobileWebUrl: 'https://developers.kakao.com',
        },
      },
    ],
  });
  return (
    <Button
      style={{
        fontFamily: 'DalseoHealingBold',
        padding: '10px',
        fontSize: '20px',
        marginTop: '2rem',
        marginLeft: '0.5rem',
        marginRight: '0.5rem',
      }}
      onClick={() => navigate('/')}
    >
      카카오톡 공유하기
    </Button>
  );
};

export default KakaoShareButton;

2. sdk를 설치하는 코드를 (https://developers.kakao.com/docs/latest/ko/getting-started/sdk-js) public > index.html 파일에 붙여넣는다. (<div id="root"> 아래에 넣기)

<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>

3. 

 

 

 

 

 

 

신기했던 점

  • 새로운 컴포넌트를 만들면 꼭 const ~ = styled.div ``; 를 넣어줘야 에러가 나지 않았다.
  • 컴포넌트를 사용하는 import문보다 라이브러리를 사용하는 import문이 아래에 있으면 에러를 낼 가능성이 있다.
    -> 그래서 순서가 import { useNavigate } from 'react-router-dom'; 가 위에, import { QuestionData } from '../assets/data/questiondata'; 가 아래로 된 것이다.

궁금했던 점

  • 버튼을 정렬할 때 align-items: center;와 justify-content: center; 이렇게 두 코드를 넣었는데 justify-content: center;만 넣어도 가운데 정렬이 되었다.
728x90
반응형