react에서 less를 사용하려면 설정을 다시 해줘야한다.

npm run eject로 숨겨진 웹팩을 꺼내서 수정을 해도 되지만 추후에 설정이 어려워질 수 있으므로 craco를 사용한다.

craco : Create React App Configuration Override

 

craco를 통한 less설치

unable to resolve dependency tree 오류 발생

$npm install @craco/craco --save --legacy-peer-deps
$npm install craco-less --save --legacy-peer-deps

unable to resolve dependency tree 에러가 발생하면 --legacy-peer-deps추가 해준다.

 

script명령어를 craco로 변경

  //pakage.json
  
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

root파일에서 craco.config.js파일 생성후 아래 코드 넣기

// craco.config.js

const CracoLessPlugin = require('craco-less');
module.exports = {
	plugins: [
		{
			plugin: CracoLessPlugin,
			options: {
				lessLoaderOptions: {
					lessOptions: {
						modifyVars: {
							'@primary-color': '#1DA57A',
							'@link-color': '#1DA57A',
							'@border-radius-base': '2px',
						},
						javascriptEnabled: true,
					},
				},
			},
		},
	],
};

https://www.npmjs.com/package/craco-less

 

craco-less

A Less plugin for craco / react-scripts / create-react-app. Latest version: 2.0.0, last published: 6 months ago. Start using craco-less in your project by running `npm i craco-less`. There are 52 other projects in the npm registry using craco-less.

www.npmjs.com

App.css -> App.less

App.js파일안에 파일확장자도 변경해서 사용하기

react는 주소에 #이 따라다닌다. '~~~/#/파일명' 이런 식으로...

단순히 git에만 배포한다면 git은 저장공간이 없어 없앨 수 없지만 서버를 사용한다면 #을 없앨 수 있다.

react/src/index.js폴더 안에서 라우터를 통해 HashRouter 대신 browserRouter 훅을 불러온 뒤 변경해주면된다.

import { BrowserRouter } from  'react-router-dom';
 
ReactDOM.render(
  <BrowserRouter>
    <App  />
  </BrowserRouter>,
  document.getElementById('root')
);

그럼 주소 뒤의 #이 제거된다.

 component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More inf~~~

원인 코드

<input
  type='text'
  value={title} // title을 불러오기전에 실행되서 나는 오류
  onChange={(e) => {
    setTitle(e.target.value);
  }}
/>

수정코드

<input
  type='text'
  value={title || ''} // 수정
  onChange={(e) => {
    setTitle(e.target.value);
  }}
/>

랜딩이 됐을때 title 값이 없으면 빈문자열을 주면 오류가 사라진다.

회원가입등 기본 유효성 검사 기본 틀 

import {useState, useEffect} form 'react';
import {useHistory} from 'react-router-dom';

useState : 상태관리 hook

useEffect : state 변경시 반영 hook

useHistory : 페이지 이동 hook

 

function Join(){
// 유효성검사를 통과하면 이동할 history hook을 변수에 지정
 const history = useHistory();
 // input에서 입력받은 값을 객체에 저장
 const initval ={
  userid:'',
  pwd:'',
  check:null
 }
 
 // 상태 지정
 const [val, setVal] = useState(initVal); // value를 initval객체 넣는 state
 const [err, setErr] = useState({}); // 유효성에서 통과를 못할 경우 해당 문구를 담을 state
 const [isSubmit,setIsSubmit] = useState(fasle); // 유효성의 통과여부를 담는 state
 
 // 유효성 검사 통과 여부를 결정지을 함수
 const validation = (val) => {
  // 에러가 나면 문구를 담을 객체 생성
  const errs = {};

 // userid의 value가 5글자 미만이면 오류를 errs객체 userid라는 key값으로 담기
  if (val.userid.length < 5) {
   errs.userid = 'id를 5글자 이상 입력하세요';
  }
  return errs;
 };

// input 요소에서 값이 바뀌면 실행될 함수
 const handleChange = (e) => {
  // 구조분해할당을 통해 e.target의 가져올 객체 값 저장
  const { name, value } = e.target;
  
  // setVal에 값을 저장, 전개연산자를 이용해 바꾸지 않을 값들을 깊은 복사
  setVal({ ...val, [name]: value });
  };
  
  // 폼데이터 전송 시 실행할 함수
  const handelSubmit = (e)=>{
   // 기본 이동 막기
   e.preventDefault();
   
   // check함수에서 유효성 검사를 거쳐 반환된 객체를 setErr 객체에 전달
   setErr(check(validation));
  };
  
   useEffect(()=>{
   // err의 값이 변경되면 객체에 담긴 개수 세기
    const len = Object.keys(err).length;
    
    // err의 객체에 값이 0개이고 isSubmit이 참이면 경로이동
    if(len === 0 && isSubmit) {
     // 메인으로 페이지 이동
     history.push('/');
    }
   },[err]);
}
return (
 <>
 // submit 전송시 실행될 함수
  <form onSubmit={handleSubmit}>
   // 객체 value값을 담으려면 항상 onChange를 써줘서 값이 변화할때마다 이벤트를 담아주고 그 값을 value로 받아와야한다. 
   <input type='text' id='userid' name='userid' value={val.userid} onChange={(e)=> handleChange(e)}>
   // err가 나면 err문구 출력
   <div className='err'>{err.userid}</div>
   
   // 버튼 클릭시 submit에 전송하겠다는 state값을 변경 -> form에서 handleSubmit으로 유효성 검사 실행되는것
   <input type='submit' vlaue='JOIN' onClick={()=>setIsSubmit(true)}>
  </form>
 </>
)

 

 

 
 
모달같이 리스트의 상태값을 자식으로 넘겨야하는 경우는 파일

부모에서 state를 생성해서 props로 자식컴포넌트에 상태값을 넘긴다.

// 부모컴포넌트 js
import Modal from '../common/Modal';
import {useState} from 'react';

function 부모(){
 const [open, setOpen] = useState(false);
 return (
  <>
   <button onClick={()=>setOpen(true)}>modal button</button>
   {open && (<Modal setOpen={setOpen}>...</Modal>)}
  </>
 )
}
// Modal.js
function Modal(props){
 return (
   <aside>
    ...
    <button onClick={()=>props.setOpen(false)}>모달닫기</button>
   </aside>
 )
}
export default Modal;

단점 

  • 부모에서 const [open, setOpen] = useState(false); 와 같은 자식요소로 전달해야하는 state를 계속 작성을 해야한다.
  • 컴포넌트 소멸 시의 이펙트를 줄 수 없다

자식 state를 부모로 넘겨주는 hook인 forwardRefuseImperativeHandle을 사용하면 공통함수를 더 관리하기 편해진다.

forwardRef : 외부컴포넌트에서 본인 컴포넌트를 참조 할수 있게 하는 hook

useImperativeHandle : 외부에서 사용할 수 있도록 state변경함수를 객체로 지정해서 내보내는 hook

// Modal.js
import {forwardRef, useState, useImperativeHandle } from 'react';

// 기존 선언식 함수에서 표현식 함수로 형태를 변경해준다.
// 외부로 참조할 수 있게 forwardRef 훅을 사용하여 wrapping해주고, 2번쨰 인자에 ref를 매개변수로 담는다.
const Popup = forwardRef((props, ref)=>{
 const [open,setOpen] = useState(false);
 
 // 외부로 내보낼 state를 담는 hook
 useImperativeHandle(ref, ()=>{
  return {
   open: ()=> setOpen(true),
   close: ()=> setOpen(false)
  }
 });
 
 return (
  <>
   {open && (
    <div>modal Content</div>
    <button onCLick={()=>setOpen(false)}>modal close</button>
   )}
   
  </>
 )

})

export default Modal;
// 부모 컴포넌트.js
import Modal from '../common/Modal';
import { useRef } from 'react';

function 부모(){
 const modal = useRef(null); // Modal 컴포넌트에서 내보낸 useImperativeHandle훅을 참조할 변수
 return (
  <>
   <button onClick={()=>modal.current.open()}></button>
   <Modal ref={modal}></Modal>
  </>
 )
}

1. git 저장소 생성

2. 저장소에 소스올리기

$ git init
$ git remote add origin 저장소주소
$ git add .
$ git commit -m '코멘트'
$ git push origin master

3. 리액트 배포

$ npm install gh-pages --save-dev
// package.json 
{
  "homepage": "https://깃아이디.github.io/프로젝트이름",
  ...
  "scripts":{
    ...
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  }
}
$ npm run deploy

4. 배포완료

리액트는 컴포넌트안에서 for문사용이 불가능하다.

리액트는 돔을 비교하여 변경 부분만 가져 오기 때문에 반복되는 요소에는 무조건 prop값으로 key값이 들어가야한다.

jsx를 반환하는 함수만들어 호출하는 방법

function App(){
  const arr = [1,2,3,4,5];
  const arrFn = ()=>{
    const result = []; 
    for(let i =0; i< arr.length; i++){
      result.push(<div key={i}>arr[i]</div>);
    };
    return result;
  };
  return <div>{arrFn()}</div>
}

map을 사용하여  return안에 작성

function App(){
  const arr = [1,2,3,4,5];
  return(
     {arr.map((data,index)=>{
       return <div key={index}>{data}</div>
     })}
  )
}

//생략형
{arr.map((data,idx)(
  <article  key={index}>{data}</article>
))()}

 import를 통한 이미지 가져오기(비권장)

import하여 가져올 이미지를 변수로 할당하여 가져오는 방법

src폴더안에 img폴더가 존재

단점 : 각각 이미지를 가져와야해서 코드가 복잡해진다.

import img1 from './img/img1.jpg';
...
return(
  <img src={img1}>
)

PUBLIC으로 이미지 가져오기

public폴더 안에 img폴더 존재

퍼블릭용 url공식(process.env.PUBLIC_URL)을 변수로 지정하여 불러온다.

const  path = process.env.PUBLIC_URL;
function App(){
    return(
      <img  src={`${path}/img/img1.jpg`}  />
    )
}

 

src가 모든 작업 소스를 넣는 곳이다.

index.js : App.js를 연결하여 불러오는 첫 페이지

App.js : 컴포넌트들을 불러와서 조립하는 페이지

 

components : jsx 파일을 분리하는 폴더

 - common, main, sub로 폴더를 구분하여 jsx파일을 각각 생성한다.

 

scss : style파일 역시 componets폴더와 동일한 구조로 나눈다.


vscode extention 설치

Prettier - Code formatter

- 코드정렬

 

ES7+ React/Redux/React-Native snippets

- 리액트 emmet 사용

 

setting.jsondp 해당 내용 추가

"emmet.includeLanguages": {
  "javascript": "javascriptreact"
},
"[html]": {
  "editor.defaultFormatter": "vscode.html-language-features"
},
"[javascript]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
  "editor.defaultFormatter": "vscode.json-language-features"
},
"[css]": {
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.tabWidth": 2,
"prettier.useTabs": true,
"prettier.vueIndentScriptAndStyle": true,
"prettier.jsxSingleQuote": true,
"prettier.singleQuote": true,
"prettier.jsxBracketSameLine": true,
"prettier.quoteProps": "consistent",
"prettier.proseWrap": "always",
"workbench.colorTheme": "Material Theme Ocean High Contrast",
"workbench.iconTheme": "vscode-icons",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true

그래도 안된다면 

설정에서 format on save -> 체크 한다


기본 작성 형태

1. 해당 컴포넌트 import로불러오기

2. function안 return 문에 식별자명을 불러온다.

- return안에는 하나의 큰 태그만 존재해야하므로 여러개의 요소를 묶을때는 <></>를 사용해서 묶어준다.

// App.js
import './scss/style.scss';
import Header from './components/common/Header';
import Footer from './components/common/Footer';

function App() {
  return (
     <>
        <Header />
        <Footer />
     </>
  );
}

스니펫 : rfce (React Funtional Component Export)

node 설치 후 버전확인 

$ npx create-react-app 프로젝트 명

맥의 경우 예외 없이 오류가 났다

여러가지를 시도했고 오류도 다양했다.

$ npm init -y

다행히 중간쯤 오류해결방법을 제시해줬다.

$ sudo chown -R 501:20 "/Users/컴퓨터이름/.npm"

Happy Hacking! 이라며 리액트 생성 완료

 

리액트 서버실행 localhost:3000 기본

$ npm start

 

오늘(2022.5.2)기준 18버전이 최신이나 리덕스를 사용하려면 17버전으로 다운그레이드를 해야한다.

$ npm i react@17 react-dom@17 --save
$ npm i react-router-dom@5 --save
$ npm i sass --save

+ Recent posts