반응형

리엑트 개인프로젝트를 진행하던 중, useEffect와 useState에대해 잘못 이해하고 있어, 

 

기록을 위해 남긴다.

 

나는 useEffect를 초기 화면 페이지 로딩 시 한번만 호출해주기 위해 사용한다고 이해하고 있었다 !

 

deps의 개념은 흐지부지하게 기억하고 있어 이슈가 발생하였는데 발생한 이슈는

 

나는 setStatus를 2개 사용하고 있다.

1. 날짜 컴포넌트 바꿀때

2. 날짜가 바뀔때 바뀐 날짜를 이용해 API를 호출해 가져와서 셋팅해줄때
버튼을 누를때마다 이게 setState가 비동기적으로 동작하여, 날짜를 찍어보니 서로 상이한 날짜를 찍고 있었다 !!

 

 

해당 부분에 대해서 어떻게 해결을할까 고민하던중 ~ useEffect의 deps의 개념이 정리된 블로그를 확인했다 !

 

useEffect(()=>{
    
 }, [count])

useEffect의 요런 코드가 있을때,

count State가 변경되면 useEffct안의 내용을 실행해주세요로 동기적으로 동작할수 있게 해준다는것을 배웠다 !!

 

그래서 setState를 두번하던 소스를 제거하고

 

  useEffect(() => {
    getUnitList()
  }, [weekIdx])

소스를 위와같이 수정하니 정상적으로 동작하는 부분을 확인했다 ~~

 

 

 

마지막으로 하나 더 useEffect에는 컴포넌트가 언마운트(사라질 때) 작업도 해줄 수 있다 

 

useEffect(() => {
    console.log('컴포넌트가 화면에 나타남');
    return () => {
      console.log('컴포넌트가 화면에서 사라짐');
    };
  }, []);

 

보통 마운트 시 (화면에 나타날때)

  • props로 받은 값을 컴포넌트의 로컬 상태로 설정
  • 외부 API요청
  • 라이브러리 사용(D3, video.js등)
  • setInterval을 통한 반복작업 혹은 setTimeout을 통한 작업 예약

언마운트 시 (화면에서 사라질때)

  • setInterver, setTimeout을 사용한 작업들 clear하기 (clearInterval, clearTimeout)
  • 라이브러리 인스턴스 제거

위 작업을 한다고 보면 될 것 같다 !

 

오늘도 많이 배운다 

 

다들 화이팅 ~

반응형

'FrontEnd > React' 카테고리의 다른 글

리엑트 배포 후 실행하기  (0) 2023.02.13
[React] React Router Dom 설치 방법  (0) 2023.02.11
리엑트 공부 재시작 !  (0) 2023.01.22
[React] Modal창 열기  (0) 2023.01.22
React - Node 연동  (0) 2023.01.22
반응형

개인 프로젝트를 진행하면서,

react-bootstrap 패키지를 활용하여 작업했다. 기록용으로 남긴다 :)

 

components/Header.js

import React, { useState } from 'react'
import { Link } from 'react-router-dom'
import { House } from 'react-bootstrap-icons'
import Modal from './Modal'
export function Header() {

  /* Modal Source */
  const [show, setShow] = useState(false)
  const openModal = () => {
    setShow(true)
  }
  const closeModal = () => {
    setShow(false)
  }
  /* Modal Source */
  
  return (
    <header>
      <div className="px-3 py-2 text-bg-dark">
        <div className="container">
          <div className="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
            <Link
              className="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none"
              to={`/`}
            >
              Home
            </Link>
            <ul className="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
              <li
                onClick={() => {
                  openModal()
                }}
              >
                <Link className="nav-link text-secondary">
                  <House size={24} className="bi d-block mx-auto mb-1" />
                  <span style={{ fontSize: 15 }}>유닛등록</span>
                </Link>
              </li>
            </ul>
          </div>
        </div>
      </div>
      
      /* Modal Source*/
      <Modal
        show={show}
        open={openModal}
        close={closeModal}
        header="유닛등록"
      />
    </header>
  )
}

export default Header

1. 나는 Header 부분 버튼에 Modal을 사용해야해서, Modal show, close를 위한 useState설정을 Header에 했다.

2. Header 부분에 Modal 컴포넌트를 붙힌다.

 

 

components/Modal.js

import React from 'react'
import Button from 'react-bootstrap/Button'
import Modal from 'react-bootstrap/Modal'
import AddUnit from './Modal_AddUnit'

//show: 보여주기 여부
//open: Header.js에서 가져온 모달을 열기 위한 props
//close: Header.js에서 가져온 모달을 닫기 위한 props
//header: 모달 Title
function ModalBody({ show, open, close, header }) {
  const handleClose = close
  let modalBody
  if (header === '유닛등록') {
    //title이 유닛등록이면 유닛등록용 ModalBody Component가져오기
    modalBody = <AddUnit />
  } else {
    //Sample
    modalBody = <Modal.Body>Sample</Modal.Body>
  }
  return (
    <>
      <Modal
        show={show}
        onHide={handleClose}
        backdrop="static"
        keyboard={false}
      >
        <Modal.Header closeButton>
          <Modal.Title>{header}</Modal.Title>
        </Modal.Header>
        {modalBody} //Custom ModalBody
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            Close
          </Button>
          <Button variant="primary">Understood</Button>
        </Modal.Footer>
      </Modal>
    </>
  )
}

export default ModalBody

1. Header.js에서 show, openModal, closeModal, Header props를 가져와서 부모로부터 모달을 컨트롤 할 수 있도록 함

2. Modal을 매번 만들기 귀찮아 Body별로 관리하기위해 분기처리하였음

 

components/Modal_AddUnit.js

import React from 'react'
import Modal from 'react-bootstrap/Modal'

function AddUnit({ header }) {
  return (
    <>
      <Modal.Body>...Add Unit Body</Modal.Body>
    </>
  )
}

export default AddUnit

Custom용으로 만든 Body이다. 요렇게 만들면

 

유닛 등록을 클릭하면 아주 잘 동작하는 것을 알 수 있다 !!!

:)

반응형

'FrontEnd > React' 카테고리의 다른 글

[Issue] useEffect, useState 버그 !  (0) 2023.01.28
리엑트 공부 재시작 !  (0) 2023.01.22
React - Node 연동  (0) 2023.01.22
[React] UseEffect  (0) 2023.01.01
ref: DOM에 이름달기  (0) 2022.10.14
반응형

이번에 리엑트 토이프로젝트를 하면서, CSS에 대해 이것저것 알아보던 중

Scss에 대해 잘 정리된 블로그가 있어 잊어먹지 않게 내 블로그에도 정리해본다 ~

Scss

Sass (Syntactically Awesome Style Sheets: 문법적으로 짱 멋진 스타일시트) 는 CSS pre-processor 로서, 복잡한 작업을 쉽게 할 수 있게 해주고, 코드의 재활용성을 높여줄 뿐 만 아니라, 코드의 가독성을 높여주어 유지보수를 쉽게한다

보통 scss 문법이 더 많이 사용되므로, 나는.scss 확장자로 스타일을 작성하겠다

 

node-sass는 지원이 끝났으므로, Dart-sass를 설치하도록 하자 !

npm install -g sass

 

Button 컴포넌트 만들기

Button이라는 컴포넌트를 만들고, Scss를 이용해서 스타일링을 하자 !

src > components 디렉터리 생성 후 그 안에 Button.js를 생성 !

 

Button.js

import React from 'react';
import './Button.scss';

function Button({ children }) {
  return <button className="Button">{children}</button>;
}

export default Button;

그 다음 같은 디렉토리에 Button.scss도 작성!

 

Button.scss

$blue: #228be6; // 주석 선언

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  height: 2.25rem;
  padding-left: 1rem;
  padding-right: 1rem;
  font-size: 1rem;

  background: $blue; // 주석 사용
  &:hover {
    background: lighten($blue, 10%); // 색상 10% 밝게
  }

  &:active {
    background: darken($blue, 10%); // 색상 10% 어둡게
  }
}

1. $blue: #228be6;와같이 변수도 사용할 수 있다

2. lighten() 또는 darken() 과 같이 색상을 더 밝게하거나 어둡게 해주는 함수도 사용 할 수 있다.

 

이제 App.js 나 사용하고자 하는 컴포넌트에서 사용하자 !

 

App.js

import React from 'react';
import './App.scss';
import Button from './components/Button';

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button>BUTTON</Button>
      </div>
    </div>
  );
}

export default App;

 

버튼이 아주 이쁘게 잘 만들어졌다.

 

 

버튼 사이즈 조정하기

우선 크기에 large, medium, small을 설정할 수 있도록 구현을 하자

Button.js에서 다음과 같이 defaultProps를 통하여 size의 기본값을 medium으로 설정하고 이 값은 button의 className에 넣자 ~

 

※ className 패키지를 사용하면 이쁘게 할 수 있으나 난 건너뜀 ~

 

classNames 샘플

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
classNames(['foo', 'bar']); // => 'foo bar'

// 동시에 여러개의 타입으로 받아올 수 도 있습니다.
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// false, null, 0, undefined 는 무시됩니다.
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

 

 

Button.js

import React from 'react';
import './Button.scss';

function Button({ children, size }) {
  return <button className={`Button ${size}`}>{children}</button>;
}

Button.defaultProps = {
  size: 'medium'
};

export default Button;

 

Button.scss

$blue: #228be6;

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    font-size: 0.875rem;
    padding-left: 1rem;
    padding-right: 1rem;
  }

  background: $blue;
  &:hover {
    background: lighten($blue, 10%);
  }

  &:active {
    background: darken($blue, 10%);
  }
  
  & + & {
  	margin-left: 1rem;
  }
}

- 위 코드에서 .Button&.large가 의미하는 것은 .Button.large와 같다.

- & + & zhemsms .Button + .Button 을의미한다. 만약 버튼이 붙어있으면, 여백을 설정하기 위함이다.

 

 

App.js에 버튼들을 2개 더 추가한다.

App.js

import React from 'react';
import './App.scss';
import Button from './components/Button';

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">BUTTON</Button>
        <Button>BUTTON</Button>
        <Button size="small">BUTTON</Button>
      </div>
    </div>
  );
}

export default App;

 

버튼이 이쁘게 잘 만들어졌다.

이번에는 색상별로 설정을 해보도록 하자.

 

 

버튼 색상 설정하기

이번에는 버튼에 파란색 외에 다른 색상을 설정하는 작업에 대해 알아보자,
버튼의 색상에는 blue, gray, pink를 설정할 수 있도록 구현할 것이다.,

※ 개발 시 색상이 고민이 들때 https://yeun.github.io/open-color/

 

Open Color

Color scheme for UI design

yeun.github.io

링크를 참고하도록 하자.

 

Button.js

import React from 'react';
import classNames from 'classnames';
import './Button.scss';

function Button({ children, size, color }) {
  return (
    <button className={classNames('Button', size, color)}>{children}</button>
  );
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue'
};

export default Button;

 

Button.scss

$blue: #228be6;
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }
  &:active {
    background: darken($color, 10%);
  }
}

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    font-size: 0.875rem;
    padding-left: 1rem;
    padding-right: 1rem;
  }

  // 색상 관리
  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }
}

 

코드의 상단에서 색상 변수를 선언해주었고, 하단에서 className에 따라 다른 색상이 적용되도록 하였다.

※ Color는 Sass에서 제공해주는 mixin이라는 기능을 이용하여 간결하게 하였다 소스를 참고해보자

App.js

import React from 'react';
import './App.scss';
import Button from './components/Button';

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">BUTTON</Button>
        <Button>BUTTON</Button>
        <Button size="small">BUTTON</Button>
      </div>
      <div className="buttons">
        <Button size="large" color="gray">
          BUTTON
        </Button>
        <Button color="gray">BUTTON</Button>
        <Button size="small" color="gray">
          BUTTON
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="pink">
          BUTTON
        </Button>
        <Button color="pink">BUTTON</Button>
        <Button size="small" color="pink">
          BUTTON
        </Button>
      </div>
    </div>
  );
}

export default App;

 

 

버튼 이미지가 잘 나왔다 .

large 이미지가 다닥 다닥 붙어 있어 보기싫다.

app.scss를 수정해주도록 하자 !

 

App.scss

.App{
	.buttons + .buttons {
    	margin-top : 1rem;
    }
}

 

이쁘게 잘 떨어져 있다 ~

 

outline 옵션 만들기

이번에는 outline 이라는 옵션을 주면 버튼에서 테두리만 보이는 설정을 해보도록 하자

- outline은 true, false여부로 설정할거라 classnames 패키지를 설치하였다 ! 참고하자

Button.js

import React from 'react'
import './Button.scss'
import classNames from 'classnames'
export function Button({ text, size, color, outline }) {
  return (
    <button className={classNames('Button', size, color, { outline })}>
      {text}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}
export default Button

Button.scss

$blue: #228be6;
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }

  &:active {
    background: darken($color, 10%);
  }
  &.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &:hover {
      background: $color;
      color: white;
    }
  }
}

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  height: 2rem;
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 0.4rem;
  font-size: 1rem;

  //사이즈 관리
  &.large {
    height: 2.4rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }
}

App.js

import React from 'react'
import Button from '../components/Button'
import SampleCss from '../components/Sample.scss'

export function Sample() {
  return (
    <div className="Sample">
      <div className="buttons">
        <Button color="blue" size="small" text="유닛등록"></Button>
        <Button text="유닛등록"></Button>
        <Button size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="gray" size="small" text="유닛등록"></Button>
        <Button color="gray" text="유닛등록"></Button>
        <Button color="gray" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록"></Button>
        <Button color="pink" text="유닛등록"></Button>
        <Button color="pink" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록" outline></Button>
        <Button color="blue" text="유닛등록" outline></Button>
        <Button color="gray" size="large" text="유닛등록" outline></Button>
      </div>
    </div>
  )
}

export default Sample

 

전체너비 버튼 생성

이번에는 fullWidth 옵션이 있으면 버튼을 전체 너비를 차지하도록 설정을 해보도록 하자 

outline과 비슷하다 !!

 

Button.js

import React from 'react'
import './Button.scss'
import classNames from 'classnames'
export function Button({ text, size, color, outline, fullWidth }) {
  return (
    <button
      className={classNames('Button', size, color, { outline, fullWidth })}
    >
      {text}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}
export default Button

Button.scss

$blue: #228be6;
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }

  &:active {
    background: darken($color, 10%);
  }
  &.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &:hover {
      background: $color;
      color: white;
    }
  }
}

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  height: 2rem;
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 0.4rem;
  font-size: 1rem;

  //사이즈 관리
  &.large {
    height: 2.4rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }

  &.fullWidth {
    width: 100%;
    justify-content: center;
    & + & {
      margin-left: 0;
      margin-top: 1rem;
    }
  }
}

App.js

import React from 'react'
import Button from '../components/Button'
import SampleCss from '../components/Sample.scss'

export function Sample() {
  return (
    <div className="Sample">
      <div className="buttons">
        <Button color="blue" size="small" text="유닛등록"></Button>
        <Button text="유닛등록"></Button>
        <Button size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="gray" size="small" text="유닛등록"></Button>
        <Button color="gray" text="유닛등록"></Button>
        <Button color="gray" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록 "></Button>
        <Button color="pink" text="유닛등록"></Button>
        <Button color="pink" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록" outline></Button>
        <Button color="blue" text="유닛등록" outline></Button>
        <Button color="gray" size="large" text="유닛등록" outline></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록" fullWidth></Button>
        <Button color="blue" text="유닛등록" outline fullWidth></Button>
        <Button color="gray" size="large" text="유닛등록" fullWidth></Button>
      </div>
    </div>
  )
}

export default Sample

 

 

이쁘다 이뻐~~

 

...rest props 전달하기

이제 우리가 버튼 컴포넌트는 다 만들었다. 만약 이 컴포넌트에 onClick을 설정해주고 싶다면 어떻게 해야할까 ?

 

Button.js 컴포넌트에 필요한 이벤트가 있을때마다 매번 이벤트를 추가하는 것은 비효율적이다.

 

이러한 이슈를 해결해줄수 있는 것이 spread와 rest이다.

 

이 문법은 주로 배열과, 객체, 함수의 파라미터 인자를 다룰때 사용하나, 컴포넌트에서도 사용할수 있다 !

 

Button.js를 다음과 같이 수정해보자 ~

 

Button.js

import React from 'react'
import './Button.scss'
import classNames from 'classnames'
export function Button({ text, size, color, outline, fullWidth, ...rest }) {
  return (
    <button
      className={classNames('Button', size, color, { outline, fullWidth })}
      {...rest}
    >
      {text}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}
export default Button

App.js

import React from 'react'
import Button from '../components/Button'
import SampleCss from '../components/Sample.scss'

export function Sample() {
  return (
    <div className="Sample">
      <div className="buttons">
        <Button
          color="blue"
          size="small"
          text="유닛등록"
          onClick={() => {
            console.log('OnClick!!!')
          }}
        ></Button>
        <Button text="유닛등록"></Button>
        <Button size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="gray" size="small" text="유닛등록"></Button>
        <Button color="gray" text="유닛등록"></Button>
        <Button color="gray" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록 "></Button>
        <Button color="pink" text="유닛등록"></Button>
        <Button color="pink" size="large" text="유닛등록"></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록" outline></Button>
        <Button color="blue" text="유닛등록" outline></Button>
        <Button color="gray" size="large" text="유닛등록" outline></Button>
      </div>

      <div className="buttons">
        <Button color="pink" size="small" text="유닛등록" fullWidth></Button>
        <Button color="blue" text="유닛등록" outline fullWidth></Button>
        <Button color="gray" size="large" text="유닛등록" fullWidth></Button>
      </div>
    </div>
  )
}

export default Sample

 

 

이렇게 ...rest를 사용해서 우리가 지정한 props를 제외한 값들을 rest라는 객체에 모아주고, 버튼 태그에 ...rest를 해주면 rest안에 있는 객체들의 값들을 모드 button에 설정해준다.

추후 비슷하게 어떤 props를 받을지는 모르지만 다른 컴포넌트 또는 HTML태그에 전달해야할 때 ...rest를 사용하도록 하자 ~~

 

고생하셨슴다 모두 !

 

 

출처 : https://react.vlpt.us/styling/01-sass.html

 

1. Sass · GitBook

01. Sass Sass (Syntactically Awesome Style Sheets: 문법적으로 짱 멋진 스타일시트) 는 CSS pre-processor 로서, 복잡한 작업을 쉽게 할 수 있게 해주고, 코드의 재활용성을 높여줄 뿐 만 아니라, 코드의 가독성을

react.vlpt.us

 



반응형
반응형
React - Node 연동

React는 그냥 프론트엔드일뿐임 ! 
Node는 Reqeust요청이오면 맞는 페이지를 전달해주면됌 ~

 

React Build

React 프로젝트를 먼저 Build하고, 프로젝트 혹은 Build 된 폴더 전체를 Node 프로젝트에 넣어준다 ~
Build방법은 아래와같음

npm run build

 

server.js

Node > server.js에 노드 화면을 라우팅해주기 위해 static 처리를 해준다 !

 

api Url > Node Static Url > 그외에 React Url 순서로 나는 맞추었다 ~

 

//Server Init 부분
    //미들웨어 셋팅
    this.setMiddleWare()

    //라우팅
    this.setRoute()

    //정적 디렉토리 추가
    this.setStatic()
    this.app.use('/public', express.static('src/public'))
    this.app.use('/uploads', express.static('uploads'))
    this.app.use(express.static(path.join(__dirname, '../react/build')))


//여기서부터 리엑트 경로로 설정해주면,
//아무것도 해당하지 않을때 리엑트 경로로 라우팅됨 !!

    //@ts-ignore
    this.app.get('/', (req, res) => {
      res.sendFile(path.join(__dirname, '../react/build/index.html'))
    })
    this.app.get('/*', (req, res) => {
      res.sendFile(path.join(__dirname, '../react/build/index.html'))
    })​

 

 

반응형

'FrontEnd > React' 카테고리의 다른 글

리엑트 공부 재시작 !  (0) 2023.01.22
[React] Modal창 열기  (0) 2023.01.22
[React] UseEffect  (0) 2023.01.01
ref: DOM에 이름달기  (0) 2022.10.14
[React] 이벤트 핸들링  (0) 2022.10.14
반응형

React는 데이터가 변할때마다 refresh 함.

이때 데이터가 변할때마다 refresh하지않고 화면 render시 초기 한번만, 혹은 특정 데이터를 바라보면서 변경하고 싶을때 

UseEffact를 사용한다.

import { useState, useEffect } from "react";
function App() {
  const [counter, setValue] = useState(0);
  const [keyword, setKeyword] = useState("");
  const onClick = () => setValue((prev) => prev + 1);
  const onChange = (event) => setKeyword(event.target.value);
  console.log("test");
  const iRunOnlyOnce = () => {
    console.log("I run only one");
  };
  useEffect(() => {
    console.log("i run only one"); //최초 한번
  }, []);
  useEffect(() => {
    if (keyword !== "" && keyword.length > 5) {
      console.log("Search For", keyword);
    }
  }, [keyword]); //키워드 변경시

  useEffect(() => {
    console.log("i run when keyword & counter change"); //키워드, 카운터 둘다 변경시 
  }, [keyword, counter]);
  return (
    <div className="App">
      <input
        value={keyword}
        onChange={onChange}
        type="text"
        placeholder="Search here..."
      />
      <p>{counter}</p>
      <button onClick={onClick}>click me</button>
    </div>
  );
}

export default App;
반응형

'FrontEnd > React' 카테고리의 다른 글

리엑트 공부 재시작 !  (0) 2023.01.22
[React] Modal창 열기  (0) 2023.01.22
React - Node 연동  (0) 2023.01.22
ref: DOM에 이름달기  (0) 2022.10.14
[React] 이벤트 핸들링  (0) 2022.10.14
반응형
리엑트의 이벤트 시스템

사용자가 웹 브라우저에서 DOM요소들과 상호 작용하는 것을 이벤트(event)라고 한다.

리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하기 때문에 사용법이 꽤 비슷하다.
하지만 주의해야 할 몇 가지 사항이 있다.

 

이벤트 사용 시 주의사항

1. 이벤트 이름은 카멜표기법으로 작성

     HTML에서는 onclick으로 작성하지만 리액트에서는 카멜 표기법으로 onClick으로 작성해야한다
2. 이벤트에는 함수 형태의 값을 전달

    HTML에서 이벤트를 설정할 때 ""안에 실행할 코드를 넣었지만, 리액트에서는 함수 형태의 객체를 전달한다. 애로우(화     살표)함수 문법을 사용하거나 혹은 외부에 미리 함수를 만들어서 전달하기도한다.

3. DOM요소에만 이벤트를 설정할 수 있다.

    div, button, input, form, span 등의 DOM요소에는 이벤트를 설정할 수 있지만, 직접만든 리액트 컴포넌트에는 이벤트를     자체적으로 설정할 수 없다.

<MyComponent onClick={doSomethine}>

    이 코드는 이름이 onClick인 props를 컴포넌트에 전달할 뿐이다.

    컴포넌트에 자체적으로 이벤트를 설정할 수는 없지만, 전달받은 props를 컴포넌트 내부의 DOM이벤트로

    설정할 수는있다.

<div onClick={this.props.onClick}>{/* ... */}</div>

리액트에서 지원하는 다양한 이벤트 종류는 아래의 링크를 통해 확인할 수 있다.

 

합성이벤트

 

합성 이벤트(SyntheticEvent) – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

 

이벤트 핸들리 예제

컴포넌트 생성 및 불러오기

src 디렉토리에 EventPractice.js 파일을 만들고 App 컴포넌트에서 불러와 렌더링한다.

 

EventPractice.js

import React, { Component } from 'react';

class eventPractice extends Component {
    render() {
        return (
            <div>
                <h1>Event Practice</h1>          
            </div>
        );
    }
}

export default eventPractice;           <h1>Event Practice</h1>

 

App.js

import React from 'react';
import EventPractice from './eventPractice';

const App = () => {
  return <EventPractice/>
}

export default App;

onChange 이벤트 핸들링하기

EventPractice 컴포넌트에 input 요소를 렌더링하는 코드와 해당 요소에 onChange 이벤트를 설정하는 코드를 작성한다.

 

import React, { Component } from 'react';

class eventPractice extends Component {
    render() {
        return (
            <div>
                <h1>Event Practice</h1>        
                <input
                 tpye="text"
                 name="message"
                 placeholder="type something"
                 onChange={e =>{
                    console.log(e);
                 }}
                 />  
            </div>
        );
    }
}

export default eventPractice;

React 실행 후 콘솔창을 킨 후 input요소에 텍스트를 입력해본다.

콘솔에 SyntheticzEvent 라는 객체가 나타나는 것을 볼 수 있는데, 이 객체는 웹 브라우저의 네이티브 이벤트를 감싸는 객체로, 이벤트 함수의 파라미터(예제 코드에서는 e)로 접근할 수 있다.

 

네이티브 이벤트와 인터페이스가 같으므로, 순수 JS에서 HTML이벤트를 다룰 때와 똑같이 사용하면되지만, 네이티브 이벤트와 달리 이벤트가 끝나면 이벤트가 초기화 되므로 정보를 참조할 수 없기 때문에ㅡ 비동기적으로 이벤트 객체를 참조해야 한다면 e.persist()함수를 호출해 주어야 한다

 

이제 state에 input값을 담아보도록 한다. state 초기값을 설정하고 이벤트 핸들링 함수 내부에서 this.setState 메소드를 호출하여 state를 업데이트 한다

 

import React, { Component } from 'react';

class eventPractice extends Component {
    state = {
        message:'',
    }
    render() {
        return (
            <div>
                <h1>Event Practice</h1>        
                <input
                 tpye="text"
                 name="message"
                 placeholder="type something"
                 value={this.state.message}
                 onChange={e =>{
                    this.setState({
                        message: e.target.value,
                    })
                 }}
                 />
                 <button
                 onClick={ () => {
                    alert(this.state.message);
                    this.setState({
                        message:'',
                    });
                 }}  
                 >
                 submit
                 </button>
            </div>
        );
    }
}

export default eventPractice;

input에 아무거나 입력 후 버튼을 누르면 해당 값이 alert으로 호출되는것을 확인 할 수 있다.

 

임의 메소드 만들기

현재의 코드는 이벤트에 실행할 함수를 렌더링 메소드 내부에 만들어서 전달해주고 있다. 이 방법 대신 함수를 미리 만들어 전달하면 가독성을 높일 수 있다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

  handleChange(e) {
    this.setState({
      message: e.target.value,
    });
  }

  handleClick() {
    alert(this.state.message);
    this.setState({
      message: '',
    });
  }

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

여기서 주의해야할 점은 this 바인딩이다. this는 함수가 어디서 호출되는지에 따라 결정된다.

클래스의 임의 메소드가 특정 HTML요소의 이벤트로 등록되는 과정에서 메도스와 this의 관계가 끊어지게된다.

 

따라서 this를 컴포넌트 자신으로 가르키게 하기 위해서는 메소드를 this와 바인딩하는 작업이 필요하고, 현재 코드에서는 constructor 함수 내부에서 함수를 바인딩하고 있다. 메소드 바인딩은 이렇게 생성자 메소드에서 하는 것이 정석이다. 새 메소드를 만들때마다 constuctor도 수정해야하기 때문이다.

하지만 클래스형 컴포넌트에서 public class fields 문법을 사용하여 화살표 함수로 메소드를 구현하면, 좀 더 편하게 작성할 수 있다. 화살표 함수의 this는 부모함수의 this를 상속받는데 JS에서 클래스는 함수로 구현되어 있기 때문에 this는 컴포넌트 자신이 되므로 bind(this)를 하지 않아도 된다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  handleChange = e => {
    this.setState({
      message: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.message);
    this.setState({
      message: '',
    });
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

이 문법은 실험적인 문법으로 바벨을 설정해주어야 하는데 Create-React-App으로 만든 프로젝트는 이 문법이 기본적으로 설정되어 있으므로 사용 가능하다.

 

input 여러개 다루기

이제 input 값을 state에 넣는 방법을 알아보았다. input이 여러개일때 방법을 알아보자. 메소드를 여러개 만들어 해결할 수 있지만, event 객체를 활용하면 더 쉽게 처리할 수 있다

onChange 이벤트 핸들러에서 e.target.name은 해당 input의 name을 가리킨다. 이값을 이용해 state를 설정하면 더 쉽게 해결할 수 있다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    username: '',
    message: '',
  };

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.username + ': ' + this.state.message);
    this.setState({
      username: '',
      message: '',
    });
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="username"
          placeholder="User name"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;
handleChange = e => {
  this.setState({
    [e.target.name]: e.target.value,
  });
};

이 코드를 보면 e.target.name을 []로 감싸는데 이렇게 객체안에서 key를 []로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key값으로 사용된다.

 

onKeyPress 이벤트 핸들링

키를 눌렀을 때 발생하는 KeyPress 이벤트를 처리해본다. 두 번째 인풋에서 Enter키를 눌렀을때 handleCllck 메소드를 호출하도록 코드를 추가한다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    username: '',
    message: '',
  };

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.username + ': ' + this.state.message);
    this.setState({
      username: '',
      message: '',
    });
  };

  handleKeyPress = e => {
    if (e.key === 'Enter') {
      this.handleClick();
    }
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="username"
          placeholder="User name"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
          onKeyPress={this.handleKeyPress}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

함수형 컴포넌트로 구현해 보기

이제 클래스형 컴포넌트로 작성했던 EventPractice 컴포넌트를 함수형 컴포넌트로 바꾸어본다.

import React, { useState } from 'react';

const EventPractice = () => {
  const [username, setUsername] = useState('');
  const [message, setMessage] = useState('');
  const onChangeUsername = e => setUsername(e.target.value);
  const onChangeMessage = e => setMessage(e.target.value);
  const onClick = () => {
    alert(username + ': ' + message);
    setUsername('');
    setMessage('');
  };
  const onKeyPress = e => {
    if (e.key === 'Enter') {
      onClick();
    }
  };

  return (
    <div>
      <h1>Event Practice</h1>
      <input
        type="text"
        name="username"
        placeholder="User name"
        value={username}
        onChange={onChangeUsername}
      />
      <input
        type="text"
        name="message"
        placeholder="type something"
        value={message}
        onChange={onChangeMessage}
        onKeyPress={onKeyPress}
      />
      <button onClick={onClick}>Submit</button>
    </div>
  );
};

export default EventPractice;

기능이 이전과 같이 동작하는지 확인해본다.

 

위의 함수형 컴포넌트 코드에서는 e.target.name을 사용하지 않고 onChange 관련 함수를 따로 만들었다. 하지만 input의 개수가 많아질수록 불편해지므로 e.target.name을 활용하는 것이 더 좋을 수 있다.

 

import React, { useState } from 'react';

const EventPractice = () => {
  const [form, setForm] = useState({
    username: '',
    message: '',
  });
  const { username, message } = form;
  const onChange = e => {
    const nextForm = {
      ...form,
      [e.target.name]: e.target.value,
    };
    setForm(nextForm);
  };
  const onClick = () => {
    alert(username + ': ' + message);
    setForm({
      username: '',
      message: '',
    });
  };
  const onKeyPress = e => {
    if (e.key === 'Enter') {
      onClick();
    }
  };

  return (
    <div>
      <h1>Event Practice</h1>
      <input
        type="text"
        name="username"
        placeholder="User name"
        value={username}
        onChange={onChange}
      />
      <input
        type="text"
        name="message"
        placeholder="type something"
        value={message}
        onChange={onChange}
        onKeyPress={onKeyPress}
      />
      <button onClick={onClick}>Submit</button>
    </div>
  );
};

export default EventPractice;

정리

리액트에서 이벤트를 다루는 것은 순수 JS또는 jQuery를 사용한 웹 애플리케이션에서 이벤트를 다루는 것과 비슷하다. 따라서 기존 HTML DOM Event를 알고 있다면 리액트의 컴포넌트 이벤트도 쉽게 다룰 수 있다.


 

 

참조

김민준님의 리액트를 다루는 기술 참조

반응형

'FrontEnd > React' 카테고리의 다른 글

리엑트 공부 재시작 !  (0) 2023.01.22
[React] Modal창 열기  (0) 2023.01.22
React - Node 연동  (0) 2023.01.22
[React] UseEffect  (0) 2023.01.01
ref: DOM에 이름달기  (0) 2022.10.14

+ Recent posts