본문 바로가기

웹_프론트_백엔드/프론트엔드

2021.03.27

1. useRef의 기능

   ① DOM에 직접 접근하는 방법
   ② 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리

 

 

2. useRef로 컴포넌트 안의 변수 만들기
└ 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리
     └ setTimeout, setInterval을 통해서 만들어진 id
     └ 외부 라이브러리를 사용하여 생성된 인스턴스
     └ scoll 위치

 

1) 배열의 항목 추가

** /component/userlist2.jsx

import React from 'react';

function User({user}) {
    return (
        <div>
            <b>{user.username}</b> <span>({user.email})</span>
        </div>
    )
}

function UserList({users}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id}/>
            ))}
        </div>
    )
}

export default UserList;

 

** /component/createUser.jsx

import React from 'react';

function CreateUser({username, email, onChange, onCreate}) {
    return (
        <div>
            <input name="username" placeholder="이름을 입력하세요" onChange={onChange} value={username}/>
            <input name="email" placeholder="이메일을 입력하세요" onChange={onChange} value={email}/>
            <button onClick={onCreate}>등록</button>
        </div>
    )
}

export default CreateUser;

 

** app.jsx

import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';

function App() {

  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });

  const {username, email} = inputs;

  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }

  const [users, setUsers] = useState([
    {
        id: 1,
        username: '김사과',
        email: 'apple@apple.com'
    },
    {
        id: 2,
        username: '오랜지',
        email: 'orange@orange.com'
    },
    {
        id: 3,
        username: '반하나',
        email: 'banana@banana.com'
    },
    {
        id: 4,
        username: '이메론',
        email: 'melon@melon.com'
    },
    {
        id: 5,
        username: '배애리',
        email: 'berry@berry.com'
    }
  ]);

  const nextId = useRef(6);

  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    }

    // 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
    // 1. spread 연산자
    // setUsers([...users, user]);

    // 2. concat 함수
    setUsers(users.concat(user));
    // --------------------------------------------------------------
    
    setInputs({
      username: '',
      email: ''
    });

    nextId.current += 1;
  }; 

  // return (
  //   <div>
  //     {/* <Counter/> */}
  //     {/* <Input/> */}
  //     {/* <MultiInput/> */}
  //     {/* <UserList/> */}
  //   </div>
  // );
  return (
    <>
    <CreateUser
      username={username}
      email={email}
      onChange={onChange}
      onCreate={onCreate}
    />
    <UserList users={users}/>
    </>
  );
}

export default App;

 

 

2) 배열의 항목 제거

** /component/userlist2.jsx

import React from 'react';

function User({user, onRemove}) {
    return (
        <div>
            <b>{user.username}</b> <span>({user.email})</span>
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

function UserList({users, onRemove}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id} onRemove={onRemove}/>
            ))}
        </div>
    )
}

export default UserList;

 

** app.jsx

import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';

function App() {

  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });

  const {username, email} = inputs;

  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }

  const [users, setUsers] = useState([
    {
        id: 1,
        username: '김사과',
        email: 'apple@apple.com'
    },
    {
        id: 2,
        username: '오랜지',
        email: 'orange@orange.com'
    },
    {
        id: 3,
        username: '반하나',
        email: 'banana@banana.com'
    },
    {
        id: 4,
        username: '이메론',
        email: 'melon@melon.com'
    },
    {
        id: 5,
        username: '배애리',
        email: 'berry@berry.com'
    }
  ]);

  const nextId = useRef(6);

  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    }

    // 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
    // 1. spread 연산자
    // setUsers([...users, user]);

    // 2. concat 함수
    setUsers(users.concat(user));
    // --------------------------------------------------------------

    setInputs({
      username: '',
      email: ''
    });

    nextId.current += 1;
  }; 

  const onRemove = id => {
    // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
    // user.id가 id인 것을 제거함
    setUsers(users.filter(user => user.id !== id));
  }

  // return (
  //   <div>
  //     {/* <Counter/> */}
  //     {/* <Input/> */}
  //     {/* <MultiInput/> */}
  //     {/* <UserList/> */}
  //   </div>
  // );
  return (
    <>
    <CreateUser
      username={username}
      email={email}
      onChange={onChange}
      onCreate={onCreate}
    />
    <UserList users={users} onRemove={onRemove}/>
    </>
  );
}

export default App;

 

 

3) 항목을 클릭하면 글자색 토글되도록 설정

** userlist2.jsx

import React from 'react';

function User({user, onRemove, onToggle}) {
    return (
        <div>
            <b
                style={{
                    cursor: 'pointer',
                    color: user.active ? 'deeppink' : 'black'
                }}
                onClick={() => onToggle(user.id)}
            >{user.username}</b> <span>({user.email})</span>&nbsp;&nbsp;
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

function UserList({users, onRemove, onToggle}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
            ))}
        </div>
    )
}

export default UserList;

 

** app.jsx

import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';

function App() {

  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });

  const {username, email} = inputs;

  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }

  const [users, setUsers] = useState([
    {
        id: 1,
        username: '김사과',
        email: 'apple@apple.com',
        active: true
    },
    {
        id: 2,
        username: '오랜지',
        email: 'orange@orange.com',
        active: false
    },
    {
        id: 3,
        username: '반하나',
        email: 'banana@banana.com',
        active: false
    },
    {
        id: 4,
        username: '이메론',
        email: 'melon@melon.com',
        active: false
    },
    {
        id: 5,
        username: '배애리',
        email: 'berry@berry.com',
        active: false
    }
  ]);

  const nextId = useRef(6);

  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    }

    // 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
    // 1. spread 연산자
    // setUsers([...users, user]);

    // 2. concat 함수
    setUsers(users.concat(user));
    // --------------------------------------------------------------

    setInputs({
      username: '',
      email: ''
    });

    nextId.current += 1;
  }; 

  const onRemove = id => {
    // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
    // user.id가 id인 것을 제거함
    setUsers(users.filter(user => user.id !== id));
  }

  const onToggle = id => {
    setUsers(
      users.map(user => user.id === id ? {...user, active: !user.active} : user)
    );
  }

  // return (
  //   <div>
  //     {/* <Counter/> */}
  //     {/* <Input/> */}
  //     {/* <MultiInput/> */}
  //     {/* <UserList/> */}
  //   </div>
  // );
  return (
    <>
    <CreateUser
      username={username}
      email={email}
      onChange={onChange}
      onCreate={onCreate}
    />
    <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
    </>
  );
}

export default App;

 

 

 

3. useEffect

 : 컴포넌트가 마운트(처음 나타났을 때) 됬을 때,
   언마운트(사라질 때) 됬을 때,
   그리고 업데이트(특정 props가 바뀔 때) 될 때 특정 작업을 처리할 수 있다.

 

** 컴포넌트가 마운트시 하는 작업

   ① props로 받은 값을 컴포넌트의 로컬 상태로 설정
   ② 외부 API 요청(REST API 등 ...)
   ③ 라이브러리 사용
   ④ setInterval을 통한 반복작업 또는 setTimeout을 통한 작업 예약

 

** 컴포넌트가 언마운트시 하는 작업

   ① 라이브러리 인스턴스 제거
   ② setInterval, setTimeout을 사용하여 등록한 작업을 clear 할 때

 

1) useEffect(함수, 배열)

   └ [함수] 처음 컴포넌트가 나타날 때 실행할 함수

   └ [배열] 의존값이 들어있는 배열, 
               만약 배열을 비우게 된다면 컴포넌트가 처음 나타낼 때만 useEffect에 등록된 함수가 호출됨

 

2) cleanup 함수

 : useEffect에서는 함수를 반환할 수 있다.
   useEffect에 대한 뒷정리를 한다. 
   deps가 비어있는 경우에 컴포넌트가 사라질 때, cleanup 함수가 호출된다.

 

** /component/userlist2.jsx

import React, {useEffect} from 'react';

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

    return (
        <div>
            <b
                style={{
                    cursor: 'pointer',
                    color: user.active ? 'deeppink' : 'black'
                }}
                onClick={() => onToggle(user.id)}
            >{user.username}</b> <span>({user.email})</span>&nbsp;&nbsp;
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

function UserList({users, onRemove, onToggle}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
            ))}
        </div>
    )
}

export default UserList;

 

 

** /component/userlist2.jsx

import React, {useEffect} from 'react';

function User({user, onRemove, onToggle}) {
    
    useEffect(() => {
        // console.log('컴포넌트가 화면에 나타남!');
        console.log(user);
        console.log('user 값이 설정되었음!');
        return () => {
            // console.log('컴포넌트가 화면에서 사라짐');
            console.log(user);
            console.log('user가 바뀌기 전...');
        }
    }, [user])

    return (
        <div>
            <b
                style={{
                    cursor: 'pointer',
                    color: user.active ? 'deeppink' : 'black'
                }}
                onClick={() => onToggle(user.id)}
            >{user.username}</b> <span>({user.email})</span>&nbsp;&nbsp;
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

function UserList({users, onRemove, onToggle}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
            ))}
        </div>
    )
}

export default UserList;

 

 

** /component/userlist2.jsx

   └ deps 파라미터 생략 : 컴포넌트가 리렌더링될 때마다 무조건 호출

import React, {useEffect} from 'react';

function User({user, onRemove, onToggle}) {
    
    // useEffect(() => {
    //     // console.log('컴포넌트가 화면에 나타남!');
    //     console.log(user);
    //     console.log('user 값이 설정되었음!');
    //     return () => {
    //         // console.log('컴포넌트가 화면에서 사라짐');
    //         console.log(user);
    //         console.log('user가 바뀌기 전...');
    //     }
    // }, [user])

    // deps 파라미터 생략 : 컴포넌트가 리랜더링될 때마다 무조건 호출
    useEffect(() => {
        console.log(user);
    });


    return (
        <div>
            <b
                style={{
                    cursor: 'pointer',
                    color: user.active ? 'deeppink' : 'black'
                }}
                onClick={() => onToggle(user.id)}
            >{user.username}</b> <span>({user.email})</span>&nbsp;&nbsp;
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

function UserList({users, onRemove, onToggle}) {
    return (
        <div>
            {users.map(user => (
                <User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
            ))}
        </div>
    )
}

export default UserList;

 

 

4. useMemo
 : 성능 최적화를 위해 연산된 값을 재사용한다.

 

** app.jsx

import React, {useRef, useState, useMemo} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';

function countActiveUser(users) {
  console.log('선택된 사용자 수를 세는 중 ... ');
  return users.filter(user => user.active).length;
}

function App() {

  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });

  const {username, email} = inputs;

  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }

  const [users, setUsers] = useState([
    {
        id: 1,
        username: '김사과',
        email: 'apple@apple.com',
        active: true
    },
    {
        id: 2,
        username: '오랜지',
        email: 'orange@orange.com',
        active: false
    },
    {
        id: 3,
        username: '반하나',
        email: 'banana@banana.com',
        active: false
    },
    {
        id: 4,
        username: '이메론',
        email: 'melon@melon.com',
        active: false
    },
    {
        id: 5,
        username: '배애리',
        email: 'berry@berry.com',
        active: false
    }
  ]);

  const nextId = useRef(6);

  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    }

    // 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
    // 1. spread 연산자
    // setUsers([...users, user]);

    // 2. concat 함수
    setUsers(users.concat(user));
    // --------------------------------------------------------------

    setInputs({
      username: '',
      email: ''
    });

    nextId.current += 1;
  }; 

  const onRemove = id => {
    // user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
    // user.id가 id인 것을 제거함
    setUsers(users.filter(user => user.id !== id));
  }

  const onToggle = id => {
    setUsers(
      users.map(user => user.id === id ? {...user, active: !user.active} : user)
    );
  }

  // return (
  //   <div>
  //     {/* <Counter/> */}
  //     {/* <Input/> */}
  //     {/* <MultiInput/> */}
  //     {/* <UserList/> */}
  //   </div>
  // );

  const count = useMemo(() => countActiveUser(users), [users]);

  return (
    <>
    <CreateUser
      username={username}
      email={email}
      onChange={onChange}
      onCreate={onCreate}
    />
    <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
    <div>선택된 사용자 수 : {count}</div>
    </>
  );
}

export default App;

 

 

 

5. Sass, Scss
1) Sass

 : CSS pre-processor로서 복잡한 작업을 쉽게 할 수 있게 해주고,
   코드의 재활용성을 높여줄 뿐만 아니라 코드의 가독성을 높여주어 유지보수를 쉽게 해준다.

 

   "yarn add node-sass"

 

** /component/button.jsx

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

/*
    HTML에서 CSS class 적용
    <div class="button size">2가지 class 적용</div>
*/
function Button({children, size}) {
    return <button className={["Button", size].join(' ')}>{children}</button>;
}

Button.defaultProps = {
    size: 'medium'
}

export default Button;

 

** /component/button.scss

$blue: #228be6;

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

    // height: 2.3rem;
    // padding-top: 0.5rem;
    // padding-left: 1rem;
    // padding-right: 1rem;
    // font-size: 1rem;

    &.large {
        height: 3rem;
        padding-top: 0.6rem;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 1.25rem;
    }

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

    &.small {
        height: 1.7rem;
        padding-top: 0.3rem;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 0.85rem;
    }

    background-color: $blue;                    // $blue 변수값을 저장
    &:hover {
        background-color: lighten($blue, 10%);  // 색상을 10% 밝게
    }
    &:active {
        background-color: darken($blue, 10%);   // 색상을 10% 어둡게
    }

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

 

** app.scss

.App {
  width: 500px;
  margin: 0 auto;
  margin-top: 4rem;
  border: 1px solid gold;
  padding: 1rem;
}

 

** app.jsx

import React from 'react';
import './app.scss';
import Button from './component/button'

function App() {
  return (
    <div className="App">
      <div>
        <Button size="large">버튼 1</Button>
        <Button>버튼 2</Button>
        <Button size="small">버튼 3</Button>
      </div>
    </div>
  );
}

export default App;

 

** index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';

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

 

 

** 색상 관련

   https://yeun.github.io/open-color

 

** /component/button.jsx

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

/*
    HTML에서 CSS class 적용
    <div class="button size">2가지 class 적용</div>
*/
function Button({children, size, color}) {
    return <button className={["Button", size, color].join(' ')}>{children}</button>;
}

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

export default Button;

 

** /component/button.scss

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

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

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

    // 1. 첫번째 size 실습
    // height: 2.3rem;
    // padding-top: 0.5rem;
    // padding-left: 1rem;
    // padding-right: 1rem;
    // font-size: 1rem;

    // 2. 두번째 size 실습
    &.large {
        height: 3rem;
        padding-top: 0.6rem;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 1.25rem;
    }

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

    &.small {
        height: 1.7rem;
        padding-top: 0.3rem;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 0.85rem;
    }

    // 1. 첫번째 color 실습
    // background-color: $blue;                    // $blue 변수값을 저장
    // &:hover {
    //     background-color: lighten($blue, 10%);  // 색상을 10% 밝게
    // }
    // &:active {
    //     background-color: darken($blue, 10%);   // 색상을 10% 어둡게
    // }

    // 2. 두번째 color 실습
    // &.blue {
    //     background-color: $blue;
    //     &:hover {
    //         background-color: lighten($blue, 10%);
    //     }
    //     &:active {
    //         background-color: darken($blue, 10%);
    //     }
    // }
    // &.gray {
    //     background-color: $gray;
    //     &:hover {
    //         background-color: lighten($gray, 10%);
    //     }
    //     &:active {
    //         background-color: darken($gray, 10%);
    //     }
    // }
    // &.pink {
    //     background-color: $pink;
    //     &:hover {
    //         background-color: lighten($pink, 10%);
    //     }
    //     &:active {
    //         background-color: darken($pink, 10%);
    //     }
    // }

    // 3. 세번째 color 실습
    &.blue {
        @include button-color($blue);
    }
    &.gray {
        @include button-color($gray);
    }
    &.pink {
        @include button-color($pink);
    }

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

 

** app.jsx

import React from 'react';
import './app.scss';
import Button from './component/button'

function App() {
  return (
    <div className="App">
      <div className="height">
        <Button size="large">버튼 1</Button>
        <Button>버튼 2</Button>
        <Button size="small">버튼 3</Button>
      </div>

      <div className="height">
        <Button size="large" color="gray">버튼 4</Button>
        <Button color="gray">버튼 5</Button>
        <Button size="small" color="gray">버튼 6</Button>
      </div>

      <div className="height">
        <Button size="large" color="pink">버튼 7</Button>
        <Button color="pink">버튼 8</Button>
        <Button size="small" color="pink">버튼 9</Button>
      </div>
    </div>
  );
}

export default App;

 

** app.scss

.App {
  width: 500px;
  margin: 0 auto;
  margin-top: 4rem;
  border: 1px solid gold;
  padding: 1rem;

  .height + .height {
    margin-top: 1rem;
  }
}

 

 

** 조건부 스타일링을 할 때 class 이름을 직접 조합해주는 것보다 편하게 작성
   "yarn add classnames"

 

   <button className="button red">
   <button className={["button", "red"].join(' ')}>


   classNames("Button", "red"); → Button red
   classNames("Button", {red: true}); → true일 때, Button red / false일때, Button
   classNames({Button: true}, {red: true}) → Button red, true일 경우만!!

 

** /component/button.jsx

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

/*
    HTML에서 CSS class 적용
    <div class="button size">2가지 class 적용</div>
*/
function Button({children, size, color}) {
    // return <button className={["Button", size, color].join(' ')}>{children}</button>;
    // classnames 사용시
    return <button className={classNames("Button", size, color)}>{children}</button>;
}

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

export default Button;

'웹_프론트_백엔드 > 프론트엔드' 카테고리의 다른 글

2021.04.03  (0) 2021.05.18
2021.03.28  (0) 2021.05.16
2021.03.21  (0) 2021.05.12
2021.03.20  (0) 2021.05.11
2021.03.20  (0) 2021.04.20