생각기록

2023-01-19 / 개발자 MBTI 조사 / React Redux 본문

강의 정리/React JS

2023-01-19 / 개발자 MBTI 조사 / React Redux

끼록관 2023. 1. 19. 19:03

React App (tetz.org)

선생님 사이트

 

이걸 통해 무엇을 배우나요!?

• 리액트 SPA(Single Page Application) 제작

• Styled-Components 활용

     - 글로벌 스타일 적용

     - 컴포넌트 디자인

• Redux 활용

 

기초 세팅

Npx create-react-app mbti-app

 

필요 모듈을 한큐에 설치

npm i redux react-redux @reduxjs/toolkit styled-components

 

폴더 구조 세팅

• Redux 활용을 위한 폴더 구조를 만들어 봅시다! 

 

 

Redux 기초 셋팅

  • src 폴더 최상위 index.js 파일 세팅
  • Vscode 의 추천 대로 createStore 가 아닌 configureStore 사용!
  • rootReducer 임포트
  • Provider 임포트 후, App 감싸주기 + store 부여
  • Redux 개발자 도구 사용을 위한 코드 추가!

rootReducer는 store/index.js 통합 모듈관리 파일임

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './store';

// 스토어 만들기
const store = configureStore({ reducer: rootReducer });

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

reportWebVitals();

 

rootReducer 설정

Src/store/index.js 에서 선언된 리듀서를 임포트 하고 있으므로 해당 파일 로 이동!

사실상 SPA(Single Page App) 이기 때문에 combineReducers 를 활용 할 필요가 없지만, 나중을 위해 연습

추후 모듈 추가시 이런 구조가 있으면 편합니다.

새로운 spa 가 추가될 경우 보통 리덕스 모듈이 추가되고, 해당 spa는 라우팅으로 구현을 한다.

*spa 싱글 페이지 어플리케이션

 

mbti store 설정

• 실제적으로 일을 하게 될, mbti store 를 설정해 봅시다!

• 초기 State 를 설정

• DB 연동을 하지 않을 것이므로 필요 데이터 설정!

• 액션 타입 설정

• 액션 함수 설정

• 리듀서 만들기

 

초기 상태 설정

• MBTI 질문 목록

• 현재 페이지 값

• Mbti 전체 결과 값

• 전체 결과에 대한 설명 값

• 추가 이미지 주소 값

페이지에 따라 보여지는 결과물이 달라지도록 할 것임

 

survey 즉, 질문 항목에 배열로 키와 값을 가진 객체들 정보를 넣엇다.

키와 값

question :

answer

 

Action Type 설정 

• 지금 App 에서 필요한 Action Type 은 어떤 것들이 있을까요?

• 먼저 페이지를 다음 장으로 넘기는 기능!

• 전달 값? → 필요 X

• 선택에 따른 결과를 반영하는 기능!

• 전달 값? → 선택에 따른 결과 값 전달 필요

• 마지막 페이지에서 결과를 리셋하는 기능!

• 전달 값? → 필요 X

 

Action 생성 함수 설정

• 외부에서 Store 내부 함수의 구조는 알 필요는 없습니다!

외부에서 원하는 Action 에 따른 기능을 Dispatch 를 통해 전달할 Action 함수를 설정해 봅시다!

• 지금 있는 Action Type 은 CHECK / NEXT / RESET 이므로 각각 Type 에 맞는 함수를 설정해 봅시다!

payload 값을 reducer에게 전달!

 

Reducer 만들기

• 이제 실제적으로 State 변경 관리하는 Reducer 를 만들어 봅시다!

Dispatch 에 의해 전달 받은 action 의 type 값에 따라 원하는 기능을 수행 하는 역할을 하면 됩니다

• Reducer 가 해당 파일의 export default 가 됩니다!

• Type 구분은 보통 Switch 를 통해 사용 합니다

• 코드 가독성이 if 문 대비 좋고, Default 가 강제 되는 부분!

 

 

CHECK

E 선택하게 되면 빈문자열에 + E 더해주고

S를 선택하면, S를 붙여주면 된다!

(문자열을 뒤에다가 하나씩 더하는 기능을 만들어 주면 된다.)

...state , 변화해줄 값

 

NEXT

state.page // ( 0 )

+ 1

 

RESET

그리고 설정한 것들을 합친다!

 

 

스토어 index.js  리덕스 통합관리 설정

 

 

컴포넌트 제작 기초 작업!

App.js 코드 정리

• React 기본 코드를 정리해 봅시다!

• 하는 김에 public 폴더의 index.html 의 주석도 정리 합시다!

이 파일은 레고의 기초판이라고 생각한다

그림요소를 직접 넣지 않고

컴포넌트를 끼워 맞추는 역할을 한다

 

 

이미지를 퍼블릭 폴더에 폴더만들어서 넣기


시작 페이지 제작(Start.jsx)

• 페이지가 로딩 되면 제일 처음 보이는 Start 페이지를 제작해 봅시다!

• 글자와 이미지, 버튼의 조합으로 간단하게 만들어 봅시다!

• 버튼은 페이지 리로딩을 막기 위해 <a>태그로 구현!

• Styled 컴포넌트를 사용하여 꾸미기!   • 컴포넌트와 분리 하기 위해 pages 폴더를 만들고 만들기!

 

Styled-components 꾸미기!

• Styled-components 를 사용해서 꾸며 봅시다!

• 각각의 태그를 별도로 이름을 변경하고, 해당 컴포넌트를 변수로 받은 다음 Styled-components 를 사용하여 디자인!

 

Start 페이지 삽입 + 메인 틀 잡기

작성한 Start 페이지를 Main 컴포넌트에 삽입 

전체 컴포넌트를 담을 컨테이너 역할을 하는 Main 컴포넌트의 스타일도 잡 아 줍시다!

 

src > app.js


Button 컴포넌트 제작

Button 컴포넌트는 테스트 시작, MBTI 선택지 선택, 다시 하기 등등 다양한 곳에서 재사용이 될 예정입니다!

• React 의 특수화 개념을 사용해서 기초 스타일인 Button 컴포넌트를 제작 하고 해당 컴포넌트를 이용하여 각각의 색과 기능을 가진 버튼으로 만들어 사용해 봅시다!

• Button 컴포넌트는 props로 부터 받아와야 할 값이 버튼의 텍스트, 이벤트 핸들러, 메인 색상, 서브 색상, Hover 시 색상의 값을 받아와야 합니다!

• MyButton 이라고 명명한 이후, Styled-components 로 꾸미기

• Styled-components 는 현재 컴포넌트에서 전달한 props 를 받아서 처리 가 가능하므로 편리하게 랜더링 시점에 디자인이 결정 되는 다이나믹 디자인 이 가능

프롭스에 따라 같은 버튼 컴포넌트로 색을 다른 버튼 구현 가능

메인 서버 호버 컬러는 

스타일드 컴포넌트에서 받기위해 쓰는 것

 

SASS 와 & 사용해서 스스로 지칭하기

css 는 중첩이 안됨

SASS는 중첩이 가능해서

자식요소들도 쓸 수 있다.

import React from 'react';
import styled from 'styled-components';
export default function Button({
  text,
  clickEvent,
  mainColor,
  subColor,
  hoverColor,
}) {
  const MyButton = styled.a`
    position: relative;
    display: inline-block;
    cursor: pointer;
    vertical-align: middle;
    text-decoration: none;
    line-height: 1.6em;
    font-size: 1.2em;
    padding: 1.25em 2em;
    background-color: ${(props) => props.mainColor};
    border: 2px solid ${(props) => props.subColor};
    border-radius: 0.75em;
    user-select: none;
    transition: transform 0.15s ease-out;
    transform-style: preserve-3d;
    margin-top: 1em;
    &::before {
      content: '';
      position: absolute;
      width: 100%;
      height: 100%;
      top: 0;
      right: 0;
      left: 0;
      right: 0;
      background: ${(props) => props.subColor};
      border-radius: inherit;
      box-shadow: 0 0 0 2px ${(props) => props.subColor};
      transform: translate3d(0, 0.75em, -1em);
    }
    &:hover {
      background: ${(props) => props.hoverColor};
      transform: translateY(0.25em);
    }
  `;

  return (
    <MyButton
      onClick={clickEvent}
      mainColor={mainColor}
      subColor={subColor}
      hoverColor={hoverColor}
    >
      {text}
    </MyButton>
  );
}

 

OrangeButton 으로 특수화

• 기본이 되는 Button 컴포넌트를 만들었으므로 특수화를 사용하여 OrangeButton 컴포넌트를 제작해 봅시다!

• 원하는 텍스트와 색상 값을 props 로 전달하고, 이벤트 핸들러는 사용 시점 에서 결정이 될 것이므로

그대로 전달만 합시다!

 

버튼컴포넌트 가져온다음에 값을 고정한다.

텍스트나 이벤트는 불러온 얘가 지정하기 때문에 PROPS로 받는다.

메인 서브 호버는 고정으로 지정한다. ( 색이 지정된 컴포넌트를 만드는 것)

app.js

가져다 적용해보자.

나온다!

 

Styled-coponents GlobalStyle

글로벌 스타일 적용!

• 리액트는 다양한 컴포넌트의 조합으로 사용이 됩니다!

• 그래서 보통 컴포넌트 단위로 디자인이 적용이 되죠

 

페이지 전체에 대한 폰트 또는 기본 스타일이 필요하다면 어떻게 하 면 될까요?

SPA 인 경우는 App.css 에 글로벌 스타일을 적용하면 되고

MPA 인 경우에는 전체를 감싸는 최종 컴포넌트에 스타일을 적용 해도 됩니다!

다만, Styled-components 의 경우는 이러한 방식보다 자체 기능을 통해 전체 페이지에 글로벌 스타일을 적용합니다

 

GlobalStyle 컴포넌트 제작

• Components 폴더에 GlobalStyle.js 파일을 만들고 GlobalStyle 컴포넌 트를 제작해 봅시다!

• Styled-components 는 createGlobalStyle 이라는 메소드를 제공하여 글로벌 스타일 적용을 가능하게 합니다!

• 필요한 것들을 설정해 봅시다!

 

일단 글로벌스타일 컴포넌트를 만들자

 

APP.JS에서 최상단에 글로벌 스타일 올려두면 끝!

오.. 신기해


페이지 분기 처리

• 지금 만드는 App 은 페이지에 따라서 보여줘야 하는 부분이 다릅니다!

• Page 가 0 이면 → Start 페이지 보여주기

• Page 가 설문의 길이와 같이 같을 때 까지→ 설문 조사 페이지 보여주기

• Page 가 설문의 길이를 넘어가면 → 결과 페이지 보여주기

 

Page 의 상태에 따라서 각각의 페이지를 렌더링 하는 방식으로 분기 처리를 해봅시다!

• 리액트의 경우 라우팅 보다는 조건부 렌더링 또는 3항 연산자, if 문으로 처 리 해주는 방법이 더 편리합니다

여기서부터 리덕스 활용

유즈셀렉터 

페이지 0 스타터 페이지

0이 아니면 설문조사 

설문의 길이를 넘어가면, 결과페이지를 보이도록 표현해보겟습니다

조건부랜더링과 삼항연산자를 써서 하면 라우팅 처리 필요없을것

 

state.mbti.page..


Mbti 페이지 제작

• 이제 설문을 하는 Mbti 페이지를 제작해 봅시다!

기존의 Button 컴포넌트를 활용해서 설문을 선택하는 SkyblueButton 컴포넌트를 작성하고 활용!

• Page 의 번호를 가져와서 해당 번호에 맞는 설문의 text 값을 SkyblueButton 에 담아서 출력 하기!

 

SkyblueButton 특수화

• 기존 OrangeButton 을 활용하여 색상 값만 변경하여 Skyblue 버튼 만들기!

 

페이지 제작

시간 관계상 프로그래스바는 피피티 보고 만드세요

이벤트 핸들러에 액션 생성 함수 지정

액션 생성 함수 지정

• 이제 페이지를 넘기는 기능을 하는 액션 생성 함수인 next() 를 dispatch 를 이용하여 Reducer 에 전달해 봅시다!

• Start 컴포넌트의 테스트 시작이라는 버튼에 지정! • Mbti 컴포넌트의 선택지 선택 버튼에 지정!

스타트 페이지 변경 해주고, 

Mbti 페이지도 이 기능을 달아버리자

디스패치가 편지가방 들고 리듀서에게 간다!

 


값을 추가하고... 

결과를 보여줘야함

Progress Bar 만들기

• 현 진행 상황을 보여주는 Progress Bar 도 만들어 봅시다!

• Progress.js 컴포넌트 작성하기

• Progress Bar 의 값은 현재 Page / 전체 설문 배열의 길이 값으로 표시하 면 됩니다!

• Progress Bar 의 바깥 부분을 먼저 그리고, 자식 요소가 부모의 크기를 상 속 한 다음 색을 입혀서 % 로 구현

page 는 현재 page state값 maxpage값은 survey.length 값

import React from 'react';
import styled from 'styled-components';

export default function Progress({ page, maxPage }) {

  return (
    <MyProgress>
      <div>
        {page} / {maxPage}
      </div>
      <Fill>
        <Gauge percent={(page / maxPage) * 100}></Gauge>
      </Fill>
    </MyProgress>
  );
}

  const MyProgress = styled.div`
    margin-top: 3em;
  `;
  const Fill = styled.div`
    width: 100%;
    height: 10px;
    background-color: #777;
    margin-top: 1em;
    text-align: left;
  `;
  const Gauge = styled.div`
    background-color: skyblue;
    display: inline-block;
    height: inherit;
    position: relative;
    top: -4px;
    width: ${(props) => props.percent}%;
  `;

Progress Bar 삽입

• 만들어진 ProgressBar 를 삽입해 줍시다

• Props 로 전달할 값은, 현재 page 값과 전체 설문의 수 이므로 해당 정보도 props 로 전달해 주면 됩니다

 


결과를 만드는 Check() 삽입!

Check() 액션 생성 함수 삽입!

• 이제 페이지는 잘 넘어 갑니다!

• 그럼 MBTI 조사 결과 값을 만들어야 겠죠!?

• 해당 기능은 CHECK Action 이 담당합니다!

 

Check() 액션 생성 함수

Check() 액션 생성 함수는 결과 값 만을 전달 받네요?

 

리듀서에서의 동작

액션 생성 함수로 전달 받은 결과를 mbtiResult 라는 결과 문자열에 추가를 해주는 액션이 끝!

=> check() 함수를 호출 할 때, 설문 객체에 포함 된 결과 문자열만 절달

 

Dispatch 로 Check() 전달!

 

동작 확인

check 로 i 문자열을 추가하고있다 1페이지

 

결과 출력 페이지 작성

PinkButton 특수화

• 기존 OrangeButton 을 활용하여 색상 값만 변경하여 PinkButton 만들 기!

 

이제 redux 에 모인 결과를 출력

• MBTI 결과가 잘 반영 되는 것을 확인 하였으니, 해당 결과를 보여줄 결과 페 이지인 Show.jsx 를 만들어 봅시다!

• Show 페이지는 Mbti 최종 결과가 들어있는 mbtiResult 값과, Mbti 결과 값에 맞는 설명 + 이미지를 출력해 주면 됩니다!

• 먼저 텍스트 부터 입력해서 디자인 부터 하고 결과 값 출력을 해봅시다

이제 리덕스에서 값 받아와!

Redux 에서 결과 값 출력하기!

• 페이지 디자인은 마쳤으니 이제 Redux 에서 결과 값을 받아서 출력해 봅시 다!

• MBTI 결과는 Store 의 mbtiResult 에 있으므로 해당 값을 받아오기

• 그리고 설명은 각각의 MBTI 결과 값을 Key 로 가지는 객체로 선언을 하였기 때문에 편리하게 접근이 가능합니다!

이해해보기

1. mbti.mbtiResult는 check() 액션 생성 함수 실행시 문자열이 추가된다

완성된 4글자를 result에 넣는다.

2. mbti.explaination. 키 > 가 그 문자열임

filter 함수 처리 필요없이 이렇게 했다.

 

Reset() 액션 생성 함수 전달

다시하기 버튼 기능 추가!

• 이제 다시하기 버튼을 눌렀을 때, 페이지가 최초로 돌아가는 기능을 추가해 주면 됩니다!

• 이미 RESET 기능(page 를 0 으로 만들고,mbtiResult 를 초기화)은 구현 이 되었으니 dispatch 를 통해 전달만 합시다

 

App.js 분기 처리

• 이제 결과 페이지도 보여줘야 하기 때문에, 결과 페이지에 대한 분기 처리도 해봅시다!

• Page 가 0 → Start 컴포넌트

• Page 가 1 ~ n → Mbti 컴포넌트

• Page 가 n + 1 → Show 컴포넌트

• If 문을 쓰는 것 보다는 간단하게 3항 연산자를 2중으로 써서 처리 해봅시다

페이지가 0 이면 스타트 페이지

page !== survey.length(4) + 1 ?           > 즉 네번의 질문 후 + 1은 결과페이지가 되어야 겠죠 / 네번의 설문 페이지 후 

즉 5페이지까지는 <Mbti />컴포넌트

아니면, <Show /> 결과 페이지