상세 컨텐츠

본문 제목

boiler-plate

CODING/React.js

by 뚜뚜 DDUDDU 2020. 8. 25. 20:26

본문

 

 

github 처음설치

github에서 새로운 레포지토리 생성(readme는 체크하지 말자! 추후 에러발생 가능)

git init

.gitignore 파일생성(파일내용: node_modules)

git add. (저장소 올리기)   // git rm --cached ~~ 저장소 올린것 다시 비우기

git status (저장소 상태보기)

git commit -m "쓸말" (로컬 저장하기)

git remote add origin master

 

변경된 내용 git에 올리기

1. git add .

2. git status에서 확인

3. git commit -m "메시지"

4. git push origin master

 

클로닝 하는 방법

git clone https://github.com/react-boilerplate/react-boilerplate.git

npm run setup

npm start

설치 프로그램: postman, nodemon(서버 접속시 자동 새로고침 기능을 위함)

npm install nodemon --save-dev --> package.json에 script 추가 --> 해당 용어로 서버 실행시킬 수 있음(start 대신)

 

비밀번호처럼 예민한 정보는 .gitignore에 옮겨야 함

 

환경변수 process.env.NODE_ENV

--> Local환경에서는development(config/dev.js)

--> Deploy(배포) 한 후에서는 production(config/prod.js)

 

 

Bcrypt 데이터 암호화

npm install bcrypt --save

 

로그인 토큰 생성을 위해서 JSONWEBTOKEN 라이브러리를 다운로드

npm install jsonwebtoken --save

 

npm install cookie-parser --save

 

프론트 폴더 구성한 다음

npx create-react-app .

(global로 받지 않고 local로 받아서 항상 최신버전 사용 가능)

 

 

폴더명 및 기능

_actions, _reducer: redux를 위한 폴더

components/view 페이지를 넣는 폴더

components/view/Section 해당 페이지 관련 css파일, 컴포넌트 삽입

app.js 라우팅 관련 일 처리.

config.js 환경 변수같은 것들 정하는 곳

hoc: higher Order Component의 약자(ex. auth권한을 통해 특정 컴포넌트 조회가능시 등 검토)

utils: 여러군데에서 쓰일 수 있는 것들을 이곳에 넣어둬서 어디서든 쓸수 있게 해줌

 

 

npm install http-proxy-middleware --save

 

백앤드와 프론트 서버를 동시에 npm start시키는 방법: concurrently

npm install concurrently --save

※ 상위폴더에서 실행시켜야 함!!

 

npm install multer --save (동영상 업로드시)

 

npm install fluent-ffmpeg(비디오 썸네일 생성) -  사전에 ffmpeg 사이트에서 설치필요

 

 

 

필요기능

※ 코딩시 나는 에러 정리할 필요 있다.

※ 레이아웃 분기

  layout.js const Layout = ({ children }) => (<div>{children}</div>);

  index.js const Index = () => (<Layout>{...}</Layout>);

※ Form에 대한 라이브러리 사용 필요! --> 방법 구상해 봐야 할 것

※ 리랜더링시: 함수내 다 실행 되지만 리턴부분 중에서 바뀐 부분만 다시그린다.(값 입력시 등)

   --> callback으로 다른값 안바뀌게 하기도

※ 객체는 { }로 감싸는 게 맞고요. ()는 그냥 모든 값을 감쌀 수 있습니다. 다만 수학에서처럼 ()로 감싼 것은 연산할 때 우선순위가 부여됩니다.

{ }로 객체를 감쌀 때 주의할 점은 화살표 함수 사용 시입니다.

const a = () => {} 이렇게 되면 마지막 {}가 객체인지 함수의 몸통인지 헷갈립니다. a = () => {} 이런 경우 함수의 몸통이고 () => ({}) 이런 경우 {} 객체를 return하는 겁니다.

실제 사이트에서는 회원가입 시 약관동의를 읽고 약관동의를 체크하는 방식인데 그런 약관동의에 대한 글들은 따로 모듈 같은게 있나요?  아님 제가 따로 작성을 해야되는건가요?
A: 모듈은 따로 쓰지는 않고 직접 작성을 많이 합니다. 다만 변경된 약관들도 모두 저장하고 있어야 해서 따로 파일로 저장해놓거나 그렇습니다. 그리고 데이터베이스상에서도 그 사람이 언제 약관동의를 했는지 시간을 기록하는 컬럼이 있어야 나중에 법적 문제가 안 생깁니다.

※ Next에서는 redux설치시 App.js의 provider 태그 필요 없음

※ 리덕스: 데이터 중앙 저장소에서 관리하기 편하게끔(컴포넌트 단계 필요없이..) (contextAPI, Mobx, Apollo(GraphQL 등)), 액션들의 히스토리 관리(불변성을 유지하면서 새로운 객체를 만들어서 생성 ...state)

리덕스의 구조
메모리 할당 부담이 덜해진다.(속성은 같지만 객체만 다르기 때문)

※ Flow: 1. front에서 이벤트 발생 --> dispatch 등으로 redux-saga로 (ex.watchLogIn) & 이와 동시에 reducer에서 switch 관련액션 동시에 실행됨(리듀서가좀빠름)--> saga에서 success되면 reducer에서 리듀서 함수 실행 --> 코드에 따라서 data 들어감 --> 그 영향이 다시 front로 건너감 ex. {isLoggedIn ? <UserProfile /> : <LoginForm />} 

(Front => Sata => redux => saga => redux? => front)

※ 리덕스 사가

import {

  all,

  fork,

  take,

  takeEvery,

  takeLatest,

  throttle,

  put,

  delay,

} from "redux-saga/effects";

import axios from "axios";

import postSaga from "./post";

import userSaga from "./user";

 

//saga's generator

//const gen = function* () {console.log(1); yield; console.log(2); yield 4;}

//const generator = gen() --> 객체생성,

//generator.next() --> gen 실행 '1', 재실행 '2'(value: 4, done: true)

//중단점이 있는 함수(yield는 멈춤, yield뒤에 값: value)

//while(true){yield '무한';} : 무한반복이 아니라 매번 중단됨('무한', done:false)

//function* watchLogin(){yield take('LOG_IN', logIn)}

 

export default function* rootSaga() {

  yield all([fork(postSaga), fork(userSaga)]);

}

 

//5. 여긴 generator함수 아님(data를 받아서 data와 함께 api에 넣음 - 바로 실행)!!

// data는 아래함수의 action.data

function logInAPI(data, a, b) {

  return axios.post("/api/login", data);

}

 

function* logIn(action) {

  //4. logIn 실행

  //6. loginAPI를 실행시켜서 result로 결과를 받음

  //put: dispatch와 같음(액션객체를 dispatch)

  //call: 동기함수실행

  //call이라 axios.post('api/login).then(()=>{yield put({type: ...})})

  //fork였다면 그냥 axios.post('api/login)와 같음

  //즉, API에 post한 다음 끝나면 다음것 put실행한다(fork면 명령내리고 받기전에 바로다음것)

  //yield는 await과 비슷한데 fork는 이를 무시하게끔 즉시실행하는 것

  try {

    // loginAPI(action.data)과 같은뜻: 함수호출 방식이 call은 이렇게

    // 첫번째자리가 함수, 그다음자리부터 매개변수(인수)

    // 즉, action에서 data꺼내서 API함수에 전달

    yield delay(1000);

    // 임시보류 const result = yield call(logInAPI, action.data, "a", "b");

    // 임시보류 yield put({ type: "LOG_IN_SUCCESS", data: result.data });

    yield put({ type: "LOG_IN_SUCCESS" });

  } catch (err) {

    //7. 요청이 실패할 경우를 위해

    yield put({

      type: "LOG_IN_FAILURE",

      data: err.response.data,

    });

  }

}

//8. 성공 결과는 result.data, 실패 결과는 err.response.data에 담김

 

//3. take: LOG_IN액션이 실행될때까지 기다리겠다. 그리고 logIn실행

//   logIn관련된 매개변수(data)가 logIn()함수의 action에 전달

//   action.type: LOG_IN_REQUEST, actin.data: login data 포함

function* watchLogIn() {

  yield takeLatest("LOG_IN_REQUEST", logIn);

}

 

function logOutAPI() {

  return axios.post("/api/logout");

}

 

function* logOut() {

  try {

    yield delay(1000);

    // 임시보류 const result = yield call(logOutAPI);

    yield put({

      type: "LOG_OUT_SUCCESS",

      // data: result.data,

    });

  } catch (err) {

    yield put({

      type: "LOG_OUT_FAILURE",

      data: err.response.data,

    });

  }

}

 

//9. yield, take 단점: 딱 한번밖에 안받음(일회용) 한번쓰면 사라져버림(따라서 while로 감싸야)

// while, take는 동기적으로 동작, takeEvery는 비동기로 동작

// 직관적이지 않고 보기에 그래서 while대신 takeEvery로 동작

// takeLatest: ex. 실수로 로그인버튼 클릭두번 따닥시 takeevery는 둘다실행하기에 대체

// 첫번째것만 하고싶으면 takeLeading

// but, 응답을 한개 취소하는거지 요청을 취소하지는 않음 (요청 2개감)

// 이를 해결하기 위한 throttle, 3000(3초내에 딱 한번만 실행)

// throttle 잘 안쓰고 보통 takelatest하고 서버에서 요청을 검토해보고 막도록 함

// 10. 서버구현 아직 안해서 call logInAPI 다 에러날 것이라 delay로 임시대체

 

function* watchLogOut() {

  yield takeLatest("LOG_OUT_REQUEST", logOut);

}

 

function addPostAPI(data) {

  return axios.post("/api/post", data);

}

 

function* addPost(action) {

  try {

    yield delay(1000);

    // 임시보류 const result = yield call(addPostAPI, action.data);

    yield put({

      type: "ADD_POST_SUCCESS",

      // data: result.data,

    });

  } catch (err) {

    yield put({

      type: "ADD_POST_FAILURE",

      data: err.response.data,

    });

  }

}

 

function* watchAddPost() {

  yield takeLatest("ADD_POST_REQUEST", addPost);

}

 

//1. all: 배열 내의 것들을 동시실행(배열을 받음)

//2. fork, call: 함수실행 명령어(fork와 call의 차이점 존재함)

//fork: 비동기함수호출(기다리지않고 바로 다음것 실행), call: 동기함수호출(기다린후 실행)

export default function* rootSaga() {

  yield all([fork(watchLogIn), fork(watchLogOut)]);

}

 

//# call, yield등을 쓰는 이유: test할 때 편함(a.next()함수실행으로 하나씩 버그검사, test코드 짜둬야 함)

 

 

 

· 컴포넌트 코딩 시작할때 순서

※ 컴포넌트상 index.js는 가장 중요하다고 생각하는 기능 삽입

※ styles.js: 스타일드컴포턴트 관련 파일

※ 배치하고 줄바꿔보고 공식문서 보고... 계속 반복

 1 return에 값 넣기(1. 큼직하게 태그 만들기 - 이부분엔 닉네임, 이부분엔 뭐다...) - antd 참고

        <NicknameEditForm />

        <FollowList header="팔로잉 목록" />

        <FollowList header="팔로워 목록" />

  2. 데이터는 대충 Dummydata 만들기

  const followerList = [

      { nickname: "제로초" },

      { nickname: "전세환" },

      { nickname: "김철수" },

  ];

   3. 세부 구현 컴포넌트 만들기(Newfile 만들어서)

    - 함수 등엔 userState, useCallback 사용 활용

 

· 폴더/파일명 나눌때

 - 컴포넌트, 컨테이너 구분 X

 - components폴더내에 form폴더, layout폴더 등등 재배치

 

· 직접적으로 스타일 간단하게 줄때(CSS)

<div style={{ marginTop: 10 }} > or

<div style={{ marginTop: '10px' }} >  이렇게 객체로 주면 안됨

(랜더링시 새로 생성되는데 객체로 주면 {} === {} false 다른값 나오므로 매번 이 부분도 리랜더링이 된다)

따라서 스타일드 컴포넌트를 주어야 함

 

만약 Antd의 태그를 새롭게 스타일하려면?

import styled from 'styled-components';

const SerchInput = styled(Input.Search)`  //Input.Search는 antd에서 제공하는 태그명임

   vertical-align: middle;

`

return(

<SerchInput enterButton></SerchInput>)

 --> 이런식으로 컴포넌트 속성 받는 방식으로 적용!!

 

이 방식도 싫다면? --> useMemo 사용

useMemo 값을 캐싱하는 것

// import {useMemo} from "react";

// const style= useMemo(()=>({marginTop: 10}),[]);

// return(<Input.Search style={style}></Input.Search>)

// 리랜더링 방지하면서 style 꾸밀수 있게끔

 

· 로그인 

 

 

· SNS로그인

 

 

· 이거 아니면 저거 보여주기

 

import UserProfile from "../components/UserProfile";

import LoginForm from "../components/LoginForm";

 

const [isLoggedIn, setIsLoggedIn] = useState(false);

 

return(

          {isLoggedIn ? <UserProfile /> : <LoginForm />}

)

 

· 링크 새창에서 열기(target="_blank" rel="noreferrer noopener" 같이써야 보안위협 사라짐)

· 반응형 그리드 &  새창에서 띄우기:

모바일 먼저 세팅해서 기준으로 한다. / 가로먼저 자르고 세로로 자른다.

import { Row, Col } from "antd";

      {/* Grid sys`  sm(테블릿)*/}

        {/* gutter: column사이의 간격*/}

        <Row gutter={8}>

          <Col xs={24} md={6} /> 왼쪽메뉴

          <Col xs={24} md={12} /> {children}

          <Col xs={24} md={6} />{" "}

          <a href="https://www.zerocho.com" target="_blank" rel="noreferrer noopener">

            Made by Zerocho

          </a>

        </Row>


· 검색창

import {  Input} from "antd";

 

return(

           <Input.Search

              enterButton

              style={{ verticalAlign: "middle" }}

            ></Input.Search>

 

※ 에러검토

TypeError: Cannot read property 'Posts' of null

--> 서버 껏다가 재시작

'CODING > React.js' 카테고리의 다른 글

회원가입 관련 Front-end와 Back-end 정리  (0) 2022.06.01
react-quill 사용(이미지 포함)방법  (0) 2022.05.29
노마드코더님 _ React #3  (0) 2020.06.16
노마드코더강의_React#1  (0) 2020.06.12
노마드코더 강의_React #2  (0) 2020.06.12

관련글 더보기

댓글 영역