Specialization는 전문화 특수화라는 뜻을 가지고있음 Specialization의 의미가 어렵다고 느껴질순있지만 예시를 보면서 이해해보자!

다이얼로그라는 것은 굉장히 범용적인 의미를 가지고있음

모든 종류의 다이얼로그를 다 포함하는 개념이라고 볼 수 있는데 웰컴 다이얼로그라는것은 누군가를 반기기위한 다이얼로그라고 볼수있음 범용적인게 아니라 좀 더 구체화된것임.

이처럼 범용적인 개념을 구별이 되게 구체화 하는것을 Specialization라고함

웰컴 다이얼로그는 다이얼로그의 특별한 케이스

기존의 객체지향 언어에서는 상속을 사용하여 Specialization을 구현함.

하지만 리액트에서는 합성을 사용하여서 Specialization을 구현함

function Dialog(props) {
	return (
    	<FancyBorder color="blue">
        	<h1 className="Dialog-title">
            	{props.title}
            </h1>
            <p className="Dialog-message">
            	{props.message}
            </p>
         </FancyBorder>
      );
  }
  
  function WelcomeDialog(props) {
  	return (
    	<Dialog
        	title="어서오세요"
            message="우리 사이트에 방문하신 것을 환영합니다"
         />
      );
  }

위에있는 Dialog라는 컴포넌트는 범용적인 의미를 가진 컴포넌트임 

그리고 이 컴포넌트를 사용하는 WelcomeDialog컴포넌트가 나옴 .

Dialog컴포넌트에서는 title과 message라는 두가지 props를 갖고있는데 각각 다이얼로그에 나오는 제목과 메세지를 의미함.

따라서 어떻게 props를 전달해주느냐에따라 메세지의 문구가 달라짐

이렇게 Specialization은 범용적으로 쓸수있는 컴포넌트를 만들어놓고 이를 특수화 시켜서 컴포넌트를 사용하는 합성방식임.

 

그렇다면 아까 배웠던 containment와 Specialization를 같이 사용하려면 어떻게 해야할까?

우선 containment를 위해서 props.children을 사용하고 Specialization을 위해 직접 정의한 props를 사용하면 됨!

 

function Dialog(props) {
	return (
    	<FancyBorder color="blue">
        	<h1 className="Dialog-title">
            	{props.title}
            </h1>
         	<p className="Dialog-message">
            	{props.message}
            </p>
            {props.children}
        </FancyBorder>
     );
  }
  
  function SignUpDialog(props) {
  	const [nickname, setNickname] = useState('');
    
    const handleChange = (event) => {
    	setNickname(event.target.value);
     }
    
    const handleSignU[ = () => {
    	alert(`어서오세요, ${nickname}님!`);
    }
    
    return (
    	<Dialog 
        	title="화성 탐사 프로그램"
            message="닉네임을 입력해주세요.">
            <input
            	value={nickname}
                onChange={handleChange} />
            <button onClick={handleSignUp}>
            	가입하기
            </button>
         </Dialog>
      );
  }

하위 컴포넌트가 다이얼로그 하단에 렌더링되고, 실제 dialog컴포넌트를 사용하는 signupdialog컴포넌트를 살펴보면 Specialization을 위한 props인 title, message에 값을 넣어주고 있으며 사용자로 부터 닉네임을 입력받고 가입하도록 유도하기 위해 <input>과 버튼 태그가 들어있습니다 이 두개의 태그는 모두 props.children으로 전달되어 다이얼로그에 표시됩니다. 이러한 형태로 containment와 Specialization을 동시에 사용할 수 있습니다!!!!

'REACT' 카테고리의 다른 글

Composition  (0) 2023.07.14
key & form  (0) 2023.07.14
Life cycle and hook  (0) 2023.07.14
Shared state  (0) 2023.07.13
React Foundation  (0) 2023.06.27

원래 composition은 구성이란 뜻을 갖고있지만 리액트에서는 합성을 의미한다.

즉 여러개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것을 말한다.

 

containment는 하위 컴포넌트를 포함하는 형태의 합성방법.

보통 사이드바나 다이얼로그 같은 박스 형태의 컴포넌트는 자신의 하위 컴포넌트를 미리 알수없음.

예시로 동일한 사이드바  컴포넌트를 사용하는 두개의 쇼핑몰이 있다고 가정해보자면, 하나의 쇼핑몰에는 의류와 관련된 메뉴가 8개 들어있고 다른 쇼핑몰에서는 식료품과 관련된 메뉴가 10개가 존재함.

이렇게 어떤게 하위 컴포넌트로 올지 모르지?

그래서 이러한 경우에는 containment방법을 사용하여 합성을 사용하게됨.

이걸 사용하는 방법은 리액트 컴포넌트의 props에 기본적으로 들어있는 children속성을 사용하면됨.

 

function FancyBorder(props) {
	return (
    	<div className={'FancyBorder FancyBorder-' +props.color>
        	{props.children}
        </div>
      );
  }

이 컴포넌트를 사용하면 자신의 하위 컴포넌트를 모두 포함하여 예쁜 테두리로 감싸주는 컴포넌트가 됨

그러면 이걸 사용하는 예제를 한번 봐보자

function WelcomeDialog(props) {
	return (
    	<FancyBorder color="blue">
        	<h1 className="Dialog-title">
            	어서와 !
            </h1>
            <p className="Dialog-message">
            	우리 사이트에 방문한걸 환영합니다
            </p>
        </FancyBorder>
    );
 }

자 이 코드를 보면 fancyborder컴포넌트로 감싸진 부분에 h1 p라는 두개의 태그가 들어가있는데 이 두 태그는 모두 fancyborder컴포넌트에 children이라는 props로 전달이됨.

-> 그럼 결과적으로 파란색의 태두리로 감싸지는 결과가 나옴!

 

이 react에서는 props.children를 통해 하위 컴포넌트를 하나로 모아서 제공해줌

but 여러개의 children 집합이 필요한 경우는 어떻게 해야할까?

이런 경우에는 별도로 props를 정의해서 각각 원하는 컴포넌트를 넣어주면됌.

 

function SplitPane(props) {
	return (
    	<div className="SplitPane">
        	<div className="SplitPane-left>
            	{props.left}
            </div>
            <div className="SplitPane-right">
            	{props.right}
            </div>
        </div>
     );
 }
 
 function App(props) {
 	return (
    	<SplitPane
        	left={
            	<Contacts />
            }
            right={
            	<Chat />
           }
        />
     );
 }

우선 맨 위에있는 SplitPane 컴포넌트는 화면을 왼쪽과 오른쪽을 분할해서 보여주는 컴포넌트이다.

여기에서 left right라는 두개의 props를 정의하여 각각 다른 컴포넌트를 넣어주고 있음 이 left right를 props로 받게되고 왼오로 분리해서 렌더링함

이렇게 여러개의 children이 필요한 경우에는 별도의 props를 정의하여서 사용하면됌.

 

이렇게 props.children이나 직접 정의한 props를 이용하여 하위 컴포넌트를 포함하는 형태로 합성하는 방법을 containment라고함.

'REACT' 카테고리의 다른 글

Specialization  (0) 2023.07.18
key & form  (0) 2023.07.14
Life cycle and hook  (0) 2023.07.14
Shared state  (0) 2023.07.13
React Foundation  (0) 2023.06.27

리액트에서의 키는 리스트에서 아이템을 구분하기 위한 고유한 문자열!

리액트에서의 키의 값은 같은 리스트에 있는 엘리먼트 사이에서만 고유한 값이면 됨.

 

그럼 실제로 고유한 키값을 어떻게 만들어서 사용해야하는지 봐보자고

이건 키값으로 숫자의 값을 사용한 것임.

const numbers =[1,2,3,4,5];
const listItems=numbers.map((number) => 
	<li key={number.toString()}>
    	{number}
    </li>
);

 

다음은 키값으로 id를 사용하는 방식

const todoItems = todo.map((todo) => 
	<li key={todo.id}>
    	{todo.text}
    </li>
);

 

 

이번엔 키값으로 인덱스를 사용하는 방법

리액트에서는 키를 명시적으로 넣어주지않으면 기본적으로 이 인덱스 값을 키값으로 사용함.

const todoItems = todos.map((todo,index) => 
	<li key={index}>
    	{todo.text}
     </li>
 );

 

그럼 이걸 활용해서 출석부 컴포넌트를 만들어보자고

 

다음은 폼이란 무엇인가?

보통 회원가입하거나 로그인할때 텍스트를 입력하는 양식 많이 보이는데 텍스트 입력뿐 아니라 체크박스나 셀렉트등 사용자가 무언가 선택을 해야 하는것 모두를 폼이라고 생각하면됨.

정리하면 사용자로부터 입력을 받기 위해 사용하는것

 

제어 컴포넌트는 사용자가 입력한 값에 접근하고 제어할 수 있도록 해주는 컴포넌트입니다.

말한 그대로 누군가의 통제를 받는 컴포넌트인데 여기서 통제를 하는게 리액트임

정리하자면 제어 컴포넌트는 그 값이 리액트의 통제를 받는 입력 폼 엘리먼트를 의미함.

제어 컴포넌트는 모든 데이터를 state에서 관리함.

 

자 그러면 사용자의 이름을 입력받는 html 폼을 리액트의 제어 컴포넌트로 만들어보자

function NameForm(props) {
	const [value, setValue] = useState('');
    
    const handleChange = (event) => {
    	setValue(event.target.value);
     }
     
     const handleSubmit = (event) => {
     	alert('입력한 이름:' + value);
        event.preventDefalut();
      }
     
     return (
     	<form onSubmit={handleSubmit}>
        	<lable>
            	이름:
                <input type="text" value={value} onChange={handleChange} />
             </lable>
             <button type="submit">제출</button>
        </form>
     )
  }

이 코드에서 input태그의 value={value}부분을 볼 수 있는데 리액트 컴포넌트의 state에서 값을 가져다 넣어주는 것이다.

그래서 state의 값이 항상 input에 표시가된다.

또한 입력값이 변경되었을때 호출되는 onChange={handleChange}처럼 handleChange함수가 호출되도록 했는데 handleChange함수에서는 setValue()함수를 사용하여 새롭게 변경된 값을 value라는 state에 저장한다.

 

만약에 사용자가 입력한 모든 알파벳을 대문자로 변경시켜서 관리하고싶으면

const handleChange = (event) => {
	setValue(event.target.value.toUpperCase());
 }

이렇게 해주면되는데 handleChange함수로 들어오는 이벤트의 타겟값을 toUpperCase() 함수를 이용하여 모두 대문자로 변경한 후에 그 값을 state에 저장하는 것이다. 이처럼 제어 컴포넌트로 사용자의 입력을 제어할수있다.

 

다음은 여러줄에 걸쳐서 나올 정도로 긴 텍스트를 입력받기 위한 html태그인 textarea태그이다.

반면에 리액트에서는 textarea태그에 value라는 attribute를 사용하여 텍스트를 표시한다.

아래 코드를 보면서 이해해보자

function RequestForm(props) {
	const [value,setValue] = useState('요청사항을 입력하세요');
    
    const handleChange = (event) => {
    	setValue(event.target.value);
     }
    
    const handleSubmit = (event) => {
    	alert('입력한 요청사항:'+value);
        event.preventDefalut();
    }
    
    return (
    	<form onSubmit={handleSubmit}>
        	<lable>
            	요청사항:
                	<textarea value={value} onChange={handleChange} />
            </lable>
            <button type="submit">제출</button>
        </form>
     )
 }

이건 이름같은 적은 텍스트의 입력을 받는게아닌 요청사항처럼 많은 텍스트의 입력을 받는 컴포넌트인데 이건 아마 곧 시작하게될 플젝에서 쓸듯..?

뭐암튼 state로는 value가 있으며 이 값을 textarea라는 태그의 value라는 attribute에 넣어줌으로써 화면에 나타나게되는데 이때 useState에서 보이는것과같이 초깃값으로 요청사항을 입력하세요를 넣어줌! 

이래서 렌더링될때부터 textarea에 텍스트가 나타남.

 

select태그

드롭다운 목록을 보여주기 위한 html태그임.

이건 여러가지 옵션중에서 하나를 선택할수있는 기능을 제공함!

html에서는 아래와같이 사용함.

<select>
	<option value="apple">사과</option>
    <option value="banana">바나나</option>
    <option value="grape">포도</option>
    <option value="watermelon">수박</option>
</select>

 

그럼 리액트에서는 어떻게 쓰일까?

function FruitSelect(props) {
	const [value,setValue] = useState('grape');
    
    const handleChange = (event) => {
    	setValue(event.target.value);
    }
    
    const handleSubmit = (event) => {
    	alert('선택한 과일:'+value);
        event.preventDefalut();
    }
    
    return (
    	<form onSubmit={handleSubmit}>
        	<label>
            	과일을 선택하세요:
                <select value={value} onChange={handleChange}>
                	<option value="apple">사과</option>
                    <option value="banana">바나나</option>
                    <option value="grape">포도</option>
                    <option value="watermelon">수박</option>
                 </select>
             </label>
             <button type="submit">제출</button>
         </form>
      )
  }

FruitSelect라는 컴포넌트가 있고이 컴포넌트의 state로 grape라는 초기값을 가진 value가 하나 있다. 

이 값을 select 태그에 value로 넣어주고 있다. 

값이 변경된 경우에는 위와 마찬가지로 handleChange함수에서 setValue함수를 사용하여 값을 업데이트한다.

이 방식을 사용하게 되면 사용자가 옵션을 선택했을때 value라는 하나의 값만을 업데이트 하면 되기 때문에 더 편리하다.

만약 다중으로 선택이 되게 하려면 아래와같이 multiple속성값을 true로 해주고 value로 선택된 옵션의 값이 들어있는 배열을 넣어주면됨.

 

<select multiple={true} value={['B','C']}>

 

file input태그

말 그대로 디바이스의 저장 장치로부터 사용자가 하나 또는 여러개의 파일을 선택할 수 있게 해주는 html태그임.

서버로 파일을 업로드하거나 자바스크립트의 file api를 사용하여서 파일을 다룰때 사용함

<input type="file" />

이런식으로 사용함.

 

그렇다면 지금까지는 하나의 컴포넌트에서 하나의 입력만을 다뤘는데 만약 하나의 컴포넌트에서 여러개의 입력을 다루기 위해서는 어떻게 해야할까?

 

이런 경우에서는 여러개의 state를 선언하여서 각각의 입력에 대해 사용하면됌.

 

앞에처럼 제어 컴포넌트에 value prop을 정해진 값으로 넣으면 코드를 수정하지 않는한 입력값을 바꿀수없음.

하지만 만약 value prop은 넣되 자유롭게 입력할 수 있게만들고싶으면 값에 undefined or null을 넣어주면됌.

 

ReactDOM.render(<input value="hi" />, rootnode);

setTimeout(function() {
	ReactDOM.render(<input value={null} />, rootNode);
 }, 1000);

처음에는 input의 값이 hi로 정해져있어서 값을 바꿀 수 없는 입력 불가 상태였다가 timer에 의해 1초뒤에 value가 null인 input태그가 렌더링 되면서 입력 가능한 상태로 바뀜.

이 방법을 잘 활용하면 value prop을 넣으면서 동시에 사용자가 자유롭게 입력 할 수 있게 만들기 가능.

 

그럼 한번 사용자 정보를 입력받는 가입 양식 컴포넌트를 한번 만들어보자

가입 입력 양식을 제출 이런식으로 간단하게 짜줌.

'REACT' 카테고리의 다른 글

Specialization  (0) 2023.07.18
Composition  (0) 2023.07.14
Life cycle and hook  (0) 2023.07.14
Shared state  (0) 2023.07.13
React Foundation  (0) 2023.06.27

state는 리액트 컴포넌트의 상태를 의미함.

쉽게 말하자면 리액트 컴포넌트의 변경 가능한 데이터를 state라고 부름. 사전에 정해진건 아니고 리액트 컴포넌트를 개발하는 각 개발자가 직접 정의해서 사용함.

중요한 점은 렌더링이나 데이터 흐름에 사용되는 값만 state에 포함 시켜야한다는 것임.

->state가변경될 경우 컴포넌트가 재렌더링 되기때문에 렌더링과 데이터흐름에 관련없는 값을 포함하면 컴포넌트가 다시 재렌더링되어 성능저하됨.

그냥 state는 자바스크립트의 객체라고 생각하면됨.

class LikeButton extends React.Component {
	constructor(props) {
    	super(props)
        this.state = {
        	liked: false
         };
      }
  }

여기서 this.state부분이 나오는데 이 부분이 바로 현재 컴포넌트의 state를 정의하는 부분임.

위에같이 class컴포넌트의 경우 state를 constructor이라는 생성자에서 정의하고

함수컴포넌트에서는 state를 useState()라는 훅을 사용하여서 정의함.

 

//잘못된 사용법(state를 직접 수정하는것)
this.state = {
	name: 'taeyoung'
 };
 
 //올바른 사용법(setState를 사용하여서 수정)
 this.setState({
 	name: 'taeyoung'
  });

이렇게 state를 직접 수정할순있지만 리액트에서 인식을 못 할 경우를 생각하여서 그냥 직접수정하지말고 setState를 사용하여서 수정해야됨.

 

리액트 컴포넌트도 사람같이 생명주기를 가지고있음.

그니까 리액트 컴포넌트가 생성되는 시점과 사라지는 시점이 정해져있다는 의미임.

출생 -> 인생 -> 사망

 

먼저 컴포넌트가 생성되는 시점인 출생시점이다 이 과정을 마운트라고 부르는데 이때 컴포넌트의 constructor(생성자)가 실행된다.

이 생성자에서는 컴포넌트의 state를 정의하게된다. 또한 컴포넌트가 렌더링되며 이후에 componentDidMount()함수가 호출이된다.

 

그 다음은 출생을 했으니 인생을 살아가겠지?

사람은 인생을 살아가는 동안 신체적, 정신적으로 변화를 겪잖아 그치? 이처럼 리액트 컴포넌트도 생애동안 변화를 겪으면서 여러번 렌더링됨.

이러한 과정을 업데이트되는 과정이라고 할수있음.

업데이트 과정에서는 컴포넌트의 props가 변경되거나 setState()함수 호출에 의해 state가 변경되거나 forceUpdate()라는 강제 업데이트 함수 호출로 인해 컴포넌트가 다시 렌더링됨.

그리고 렌더링 이후에 componentDidUpdate()함수가 호출됨.

 

그리고 마지막으로는 사망과정

사람은 누구나 나이를 먹고 죽잖아 ㅇㅈ?

리액트 컴포넌트 또한 이 과정을 거치게되는데 이 과정을 언마운트라고 부름.

그럼 컴포넌트는 언제 언마운트가 될까? 바로 상위 컴포넌트에서 현재 컴포넌트를 더 이상 화면에 표시하지 않게 될때 언마운트 된다고 볼 수 있음.

이때 언마운트 직전에 componentWillUnmount()함수가 호출됨.

이 생명주기에서 가장 중요한것은 컴포넌트가 계속 존재하는 것이 아니라 시간의 흐름에 따라 생성되고 업데이트되다가 사라진다는 것임.

 

 

프로그래밍에서 훅은 원래 존재하는 어떤 기능에 마치 갈고리를 거는 것처럼 끼어 들어가 같이 수행되는 것을 의미한다.

리액트에서의 훅도 마찬가지로 리액트의 state와 생명주기 기능에 갈고리를 걸어 원하는 시점에 정해진 함수를 실행되도록 만든것. 그리고 이때 실행되는 함수를 훅이라고 부르기로 정함!

 

이런 훅의 이름은 모두!! use로 시작됨!

훅이 수행하는 기능에 따라서 이름을 짓게 되었는데 각 기능을 사용하겠다는 의미로 use를 앞에 붙임.

 

뭐 대표적인 훅으로는 useState를 들수있다. state를 사용하기 위한 훅이고 

코드를 보면서 이해해보자!

import React, {useState} from "react";

function Counter(props) {
	var count = 0;
    
    return (
    	<div>
        	<p>총 {count}번 클릭했습니다.</p>
            <button onClick={() => count++}>
            	클릭
            </button>
         </div>
     );
}

이건 Counter이라는 함수 컴포넌트이다 딱 보면 버튼 클릭하면 카운트 하나씩 증가시키고 현재 카운트 횟수를 보여주는 간단한 컴포넌트이다.

근데 만약에 이렇게 위해 코딩한거마냥해버리면 버튼 클릭하면 카운트 값이 증가는 되겠지만 재렌더링이 되지않기에 화면에 새로운 카운트 값이 표시가되지않는다. 따라서 이런 경우에는 state를 사용하여서 값이 바뀔때마다 재렌더링을 시켜주어야한다.

 

근데 class컴포넌트가 아닌 함수컴포넌트에는 이런 해당 기능이 따로 없기에 useState를 사용하여서 state를 선언하고 업데이트를 해야한다!

const[변수명, set함수명] = useState(초깃값);

useState()는 다음과 같이 사용이된다!

 

useState()를 호출할때에는 파라미터로 선언할 state의 초깃값이 들어간다.이렇게 초깃값을 넣어 useState()를 호출하면 리턴값으로 배열이 나온다 리턴된 배열에는 두가지 항목이있는데 첫 항목은 state로 선언된 변수이고 두번째 항목은 state의 set함수임.

import React, {useState} from "react";

function Counter(props) {
	const[count, setCount] = useState(0);
    
    return (
    	<div>
        	<p>총 {count}번 클릭했습니다.</p>
            <button onClick={() => setCount(count + 1)}>
            	클릭
             </button>
         </div>
      );
 }

이렇게 useState()를 사용하여 count값을 state로 관리하는건데 이 코드에서 state의 변수명과 함수가 각각 count, setCount로 되어있는것을 볼 수 있지?

버튼 누르면 setCount()함수 호출해서 카운트 1증가시킴 그리고 count값 변경되면 컴포넌트 재렌더링되면서 새로운 카운트값 표시됨.

 

또 많이 쓰이는 훅은 useEffect()!

이 이팩트라는것은 서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 등의 작업을 말함 왜 이걸 이팩트라고 불러? 이 작업들이 다른 컴포넌트에 영향을 미칠수있고, 렌더링중에는 작업이 완료될수없기때문임!

 

이 useEffect는 클래스 컴포넌트에서 제공하는 생명주기 함수인 componentDidMount(), componentDidUpdate(), componentWillUnmount()와 동일한 기능을 하나로 통합해서 제공함 그래서 useEffect()훅만으로 생명주기 함수와 동일한 기능 수행 가능

useEffect(이팩트 함수, 의존성 배열);

첫번째 파라미터로는 이팩트 함수가 들어가고 두번째 파라미터로는 의존성 배열이 들어갑니다.

의존성 배열이란 말 그대로 이 이펙트가 의존하고있는 배열인데 배열안에 있는 변수중에 하나라도 값이 변경되었을때

이펙트 함수가 실행됩니다.

import React, {useState, useEffect} from "react";

function Counter(props) {
	const[count, setCount] = useState(0);
    
    useEffect(() => {
    //브라우저 API를 사용하여 document의 title을 업데이트
    	document.title = `총 ${count}번 클릭했습니다.`;
      });
      
      return (
    	<div>
        	<p>총 {count}번 클릭했습니다.</p>
            <button onClick={() => setCount(count + 1)}>
            	클릭
             </button>
         </div>
      );
 }

아까 사용했던 useState훅에 추가로 useEffect()훅을 사용한것이다. useEffect()함수를 사용하여 생명주기 함수의 기능을 동일하게 수행하도록 만든것인데 useEffect()안에 있는 이펙트 함수에서는 브라우저에서 제공하는 API를 사용해서 document의 title을 업데이트한다. 이 title은 크롬 브라우저에 나오는 탭의 제목이담.

 

위에처럼 useEffect를 의존성 배열없이 사용한다면 처음 컴포넌트가 마운트 되었을때 실행되고, 이후 컴포넌트가 업데이트 될때마다 실행된다.

또한 이 이펙트 함수는 컴포넌트 안에서 선언되자나 맞지? 그래서 props와 state에도 접근가능

보면 count라는 state에 접근할수있잖아.

 

그렇다면 componentWillUnmount()와 동일한 기능은 useEffect로 어떻게 구현이가능할까?

import React, {useState, useEffect} from "react";

function UserStatus(props) {
	const [isOnline, setIsOnline] = useState(null);
    
    function handleStatusChange(status) {
    	setIsOnline(status.isOnline)
      }
      
      useEffect(() => {
      	ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
        return () => {
        	ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
         };
      });
      
      if(isOnline === null) {
      	return '대기중...';
      }
      
      return isOnline ? '온라인' : '오프라인';
   }

 

음 이 useEffect라는게 나도 아직 이해가 잘 안되는부분이라 다시정리할거임 그렇게 알아 ㅎㅎ

이걸 사용함으로써 생명주기 메소드를 함수형 컴포넌트에서도 사용할수있다는거잖아

useEffect()!!!!!!!!!!!!!!!!!!!!!!!

리액트 컴포넌트가 렌더링 될때마다 특정 작업(사이드 이펙트)을 실행할 수 있도록 하는 리액트 훅!!!!

여기서 사이트 이펙트란 컴포넌트가 렌더링 된 이후에 비동기로 처리되어야하는 부수적인 효과들을 뜻함.

자자 기본형태부터 아까 한거부터 다시보자

useEffect(function, deps)

자 여기서 function은 실행하고자하는 함수일거고,

deps는 배열형태이자 function을 실행시킬 조건이야

deps에 특정값을 넣으면 컴포넌트가 mount될때 지정한 값이 업데이트가 될때! -> useEffect를 실행하는거야

자 그러면 이때 deps부분을 생략한다면 어떻게될까? -> 그럼 해당 컴포넌트가 렌더링 될때마다 useEffect가 실행되게돼.

useEffect(() => {
	console.log("렌더링 될때마다 실행");
  });

만약에 맨 처음 렌더링될때 한번만 실행하고 싶으면 deps위치에 빈 배열을 넣어주면돼!!

useEffect(() => {
	console.log("맨 처음 렌더링 될때 한번만 실행")
 },[]);

 

만약에 특정 값이 업데이트 될때만 실행하고 싶을땐 deps위치의 배열안에 실행 조건을 넣어주면됨!

useEffect(() => {
	console.log(name);
    console.log("name값이 업데이트 될때만 실행");
 },[name]);

 

근데 이렇게하면 업데이트될때뿐만 아니라 마운트 될때도 실행되므로 만약에 업데이트될때만 실행시키고싶으면 아래처럼 하면돼!

const mounted = useRef(false);
useEeefct(() => {
	if(!mounted.current) {
    	mounted.current = true;
        } else {
        	console.log(name);
            console.log("업데이트 될때마다 실행");
          }
        },[name]);

 

자 그러면 어떤 예시로 쓰이는지 한번 봐보자고!

import React, {useEffect, useState} from "react";

function UseEffect() {
	const [name,setName] = useState("초기 닉네임");
    
    useEffect(() => {
    	console.log("컴포넌트 나타남");
        console.log(name);
        return () => {
        	console.log("clean up함수");
          };
        });
        
        const onClick = () => {
        	setName("닉네임 변경");
          };
          return (
          	<div>
            	{name} <button onClick={onClick}>변경</button>
            </div>
         );
      }
      
      export default UseEffect;

 

 

useMemo는 Memoized value를 리턴하는 훅임.

파라미터로 create함수와 의존성 배열을 받는다.

의존성 배열에  들어있는 변수가 변했을 경우에만 새로 create함수를 호출하여 결괏값을 반환하며, 그렇지 않은 경우에는 기존 함수의 결괏값을 그대로 반환함.

이걸 사용하면 컴포넌트가 다시 렌더링 될때마다 연산량이 높은 작업을 반복하는 것을 피할수있음.

-> 결과적으로 빠른 렌더링 속도를 얻을 수 있음.

const memoizedValue = useMemo(
	() => {
        //연산량이 높은 작업을 수행하여 결과를 반환
    	return computeExpensiveValue(의존성 변수1, 의존성 변수2);
      },
      [의존성 변수1, 의존성 변수2]
  );

 

useCallback() 훅은 useMemo와 유사한 역할을 함

근데 이 두가지의 차이점은 값이  아닌 함수를 반환한다는점.

마찬가지로 함수와 의존성 배열을 파라미터로 받음!

useCallback()훅에서는 파라미터로 받는 함수를 콜백이라고 부름.

그리고 의존성 배열에있는 변수 중 하나라도 변경되면 메모이제이션된 콜백함수를 반환함.

 

const memoziedCallback = useCallback(
	() => {
    	doSomething(의존성 변수1, 의존성 변수2);
     },
     [의존성 변수1, 의존성 변수2]
 );

만약 이 훅을 사용하지않고 컴포넌트 내에 함수를 정의한다면 매번 렌더링이 일어날때마다 함수가 새로 정의됨.

따라서 useCallback()을 사용하여 특정 변수의 값이 변한 경우에만 함수를 다시 정의하도록하여 불필요한 반복작업을 없애주는것임.

 

useRef()훅은 래퍼런스를 사용하기 위한 훅

여기서 래퍼런스란? 특정 컴포넌트에 접근 할 수 있는 객체를 말함!

 

'REACT' 카테고리의 다른 글

Composition  (0) 2023.07.14
key & form  (0) 2023.07.14
Shared state  (0) 2023.07.13
React Foundation  (0) 2023.06.27
REACT  (0) 2023.04.29

말 그대로 공유된 state를 의미함.

어떤 컴포넌트의 state에 있는 데이터를 여러개의 하위 컴포넌트에서 공통적으로 사용하는 경우를 말함.

어떤 값이 있는 컴포넌트가 있으면 그것에 대하여 그 값에 2를 곱해서 표시하는 컴포넌트와 값이 3을 곱해서 표시하는 컴포넌트 이렇게 두가지 경우가 되는 것처럼..

또 다른 예시를 들어보자면 온도를 받으면 그 온도값에 대해서 온도를 섭씨로 표현하는 컴포넌트와 온도를 화씨로 표현하는 컴포넌트 정도..?

이렇게 지금까지 살펴본것처럼 하위 컴포넌트가 공통된 부모 컴포넌트의 state를 공유하여 사용하는 것을 shared state라고 함.

 

그럼 이제 하위 컴포넌트에서 state 공유하는 것에 대해서 말해보자

아까 예시를 든 온도를 받아서 화씨와 섭씨로 표현해주는 컴포넌트를 만들어보겠음!

우선 섭씨 온도 값을 prop으로 받아서 물이 끓는지 안끓는지를 문자열로 출력해주는 컴포넌트를 만들어보면

function BoilVerdict(props) {
	if (props.celsius >= 100) {
    	return <p>물이 끓습니다.</p>;
        }
      return <p>물이 끓지 않습니다.</p>;
  }

그럼 이제 이 만든 컴포넌트를 실제로 사용하는 부모 컴포넌트를 만들어보자

function Calculator(props) {
	const [temperature, setTemperature] = useState('');
    
    const handleChange = (event) => {
    	setTemperature(event.target.value);
     }
   
   	return (
    	<fieldset>
        	<legend>섭씨 온도를 입력하세요:</legend>
            <input
            	value={temperature}
                onChange={handleChange} />
            <BoilingVerdict
            	celsius={parseFloat(temperature)} />
          </fieldset>
       )
  }

이렇게 아까 만든 컴포넌트에 온도를 입력받고 prop으로 온도를 전달해줘서 온도에 따라서 물이 끓는지 안끓는지를 출력하는 것을 짜보았다.

 

그럼 리액트에서의 장점인 재사용성을 올리기위해서 온도를 입력받는 부분도 따로 컴포넌트로 빼와서 만들어줘보겠음.

 

const scaleNames = {
	c: '섭씨',
    f: '화씨',
 };
 
function TemperatureInput(props) {
	const [temperature, setTemperature] = useState('');
    
    const handleChange = (event) => {
    	setTemperature(event.target.value);
     }
    	
    return (
    	<fieldset>
        	<legend>온도를 입력해주세요(단위:{scaleNames[props.scale]}):</legend>
            <input value={temperature} onChange={handleChange} />
         </fieldset>
      )
  }

이렇게 온도를 입력받는 부분을 추출하였는데 추가적으로 props에 단위를 나타내는 scale을 추가하여 온도의 단위를 섭씨나 화씨로 입력 가능하도록 하였다.

 

그럼 이제 섭씨온도와 화씨온도를 동기화 시키기 위하여 각각 변환하는 두 함수를 만들어주자

function toCelsius(fahrenheit) {
	return (fahrenheit - 32) * 5 /9;
 }

function toFahrenheit(celsius) {
	return (celsius * 9 /5) +32;
 }

이렇게 만든 함수를 호출하는 함수를 만들어보자

function tryConvert(temperature, convert) {
	const input = parseFloat(temperature);
    if(Number.inNaN(input)) {
    	return '';
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}

이 함수는 첫번째 파라미터로 온도값을 받고 두번째 파라미터로 변환하는 함수를 받아서 변화된 값을 리턴 시켜주는 함수임!

 

tryConvert('abc', toCelsius)
//이렇게 문자열이 들어가면 empty string 출력
tryConvert('10.22', toFahrenheit)
//이러면 변화된 값인 50.396리턴

다음은 이러한 하위 컴포넌트의 state를 공통된 부모 컴포넌트로 올려서 shared state를 적용해야 함.

여기서 state를 상위 컴포넌트로 올린다는것은? state끌어올리기라고 표현함.

그러면 TemperatureInput 컴포넌트에서 온도 값을 가져오는 부분을 아래와  같이 수정해야함.

return (
	//전 코드: <input value={temperature} onChange={handleChange} />
	<input value={props.temperature} onChange={handleChange} />
    ...
    )

이렇게 하면 온도값을 컴포넌트의 state에서 가져오는 것이 아니라 props를 통해서 가져오게됨 

또 컴포넌트의 state를 사용하지 않게되기때문에 입력값이 변경되었을때 상위 컴포넌트로 그 변경된 값을 전달해주어야하기때문에 handleChange()함수를

const handleChange = (event) => {
	//변경전: setTemperature(event.target.value);
    props.onTemperatureChange(event.target.value);
    }

이렇게 사용자가 온도 값을 변경할때마다 props에 있는 onTemperatureChange()함수를 통해 변경된 온도 값이 상위 컴포넌트로 전달되게끔 해줘야함

 

최종 완성본

function TemperatureInput(props) {
	const handleChange = (event) => {
    	props.onTemperatureChange(event.target.value);
     }
    
   return (
   	<fieldset>
    	<legend>온도를 입력해주세요(단위:{scaleNames[props.scale]}): </legend>
        
        <input value={props.temperature} onChange={handleChange} />
     </fieldset>
  )
}

마지막으로 변경된 TemperatureInput 컴포넌트에 맞춰서 Calculator 컴포넌트를 변경해주어야함

function Calculator(props) {
	const [temperature, setTemperature] = useState('');
    const [scale, setScale] = useState('c');
    
    const handleCelsiusChange = (temperature) => {
    	setTemperature(temperature);
        setScale('c');
     }
    
    const handleFahrenheitChange = (temperature) => {
    	setTemperature(temperature);
        setScale('f');
     }
    
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
    
    return (
    	<div>
        	<Temperature
            	scale = "c"
                temperature={celsius}
                onTemperatureChange={handleCelsiusChange} />
           
           <Temperature
            	scale = "f"
                temperature={fahrenheit}
                onTemperatureChange={handleFahrenheitChange} />
           
           <BoilingVerdict
           	celsius={parseFloat(celsius)} />
            
         </div>
      );
   }

 

자 그럼 이걸 실제로 만들어볼거임!

 

이런식으로 온도를 입력받는 컴포넌트를 만들어주고

 

이렇게 온도를 바꿔서 출력해주는 컴포넌트를 만듬!

 

결과물!

끝!!

'REACT' 카테고리의 다른 글

key & form  (0) 2023.07.14
Life cycle and hook  (0) 2023.07.14
React Foundation  (0) 2023.06.27
REACT  (0) 2023.04.29
React  (0) 2022.11.09

실제로 각 페이지별로 html 파일이 따로 존재하며 페이지를 이동하게 될 경우 브라우저에서는 해당 페이지의 html파일을 받아와서 화면에 표시해준다!!

근데 페이지가 수많아지면 어떻게 관리를 해야하지?

이런 문제를 해결하기 위하여 나온 것이 바로 SPA(single page application)임.

이건 말그대로 하나의 페이지만 존재하는 웹 또는 어플리케이션임. 페이지가 여러개면 MPA 하나면 SPA (처음에는 이 HTML파일의 바디태그 내부가 텅 비어있다가 해당 페이지에 접속할때 그 페이지에 해당하는 콘텐츠를 가져와서 동적으로 BODY태그 내부를 채워넣게됨.

 

자바스트립트의 함수

함수는 입력을 받아서 정해진 출력을 하는 것

여기서 함수의 입력을 파라미터 또는 인자라고함

자바스크립에서 함수를 정의하는 방법은 두가지인데 첫번째는 FUNCTION SATEMENT이고 두번째는 ARROW FUNCTION EXPRESSION을 사용하는 방법임.

 

FUNCTION STATEMENT를 사용하였을때

function sum(a,b) {
	return a+b;
}

 

ARROW FUNCTION EXPRESSION을 사용하였을때

const multiply = (a,b) => {
	return a*b;
}

 

리액트는 자바스크립트 라이브러리임 라이브러리는 자주 사용되는 기능을 정리해 모아 놓은 것

그럼 사용자 인터페이스를 만들기 위한다는것은 무슨 의미일까 

사용자 인터페이스는 UI라고 하는데 이건 사용자와 컴퓨터 프로그램이 서로 상호작용을 하기 위해 중간에서 서로간에 입력과 출력을 제어해주는 것이 바로 UI임.

걍 우리가 보는 버튼이나 텍스트 입력창같은거임. 히히

리액트는 대표적인 자바스크립트 UI 라이브러리임.

 

뭐 정리하자면 리액트는 사용자와 웹사이트의 상호작용을 돕는 인터페이스를 만들기 위한 자바스크립트 기능 모음집 정도!

 

리액트는 빠른 업데이트를 위해 내부적으로 Virtual DOM이라는 것을 사용함.

쉽게 설명해보자면 Virtual DOM은 말 그대로 가상의 DOM임. DOM은 DOCUMENT OBJECT MODEL의 약자로 웹페이지를 정의하는 하나의 객체라고 생각하면됨. 

쉽게말해서 하나의 웹사이트에 대한 정보를 모두 담고 있는 큰 그릇이라고 보면됨.

 

기존의 방식으로 화면을 업데이트 하려면 DOM을 직접 수정해야하는데 이것은 성능에 영향을 크게 미치고 비용도 굉장히 많이듬. (수정할 부분을 DOM의 데이터에서 모두 찾아야 하기 때문)

하지만 리액트는 DOM을 직접 수정하는 것이 아니라 업데이트해야할 최소한의 부분을 검색하고 검색된 부분만을 업데이트하고 다시 렌더링하면서 변경된 내용을 빠르게 사용자들에게 보여줌.

 

컴포넌트는 구성요소라는 뜻. 리액트는 모든 페이지가 컴포넌트로 구성되어있고, 하나의 컴포넌트는 또 다른 여러개의 컴포넌트의 조합으로 구성될 수 있음. 따라서 리액트로 개발을 하다보면 레고블록을 조립하는 것처럼 컴포넌트를 조합해서 웹사이트를 개발하게됨. 

 

재사용성! 객체지향프로그래밍에서 등장하는데 소프트웨어 개발에 있어서 매우 중요함. 이건 다시 사용이 가능한 성질인데 이 재사용성은 의존성 문제말고도 여러가지 호환성 문제도 발생할수있음 따라서 재사용성이 높게 개발하는것이 좋음

다시 말하자면 다른 모듈에 의존성을 낮추고 호환성 문제가 발생하지 않도록 개발해야한다!!

 

이러한 재사용성이 높아지면 개발 기간이 단축됨. 기존에 개발해둔 모듈을 곧바로 재사용하여 개발하면 되기때문에 개발기간을 많이 줄일수있음. 

또한 유지보수가 용이함. 소프트웨어에서 공통으로 사용하는 모듈에 문제가 생기면 해당 모듈만 수정해서 다시 배포하면되기때문!

이렇게 재사용성이 높게 개발을 하면 버그를 찾기에도 훨씬 수월함. 재사용성이 높다? -> 모듈간의 의존성이 낮다

따라서 리액트 컴포넌트를 개발할때 항상 쉽고 재사용 가능한 형태로 개발하는 것이 중요함.

 

근데 리액트는 높은 상태 관리 복잡도를 가지고있음. state라는 개념이 있는데 쉽게말해 리액트 컴포넌트의 상태를 의미함. virtual dom은 바뀐 부분만을 찾아서 업데이트한다고 했는데 바뀐 부분이라는 것은 state가 바뀐 컴포넌트를 의미함. 따라서 state는 리액트에서 중요한 부분임.

성능 최적화를 위해 state를 잘관리하는 것이 중요함. 

따라서 상태관리를 할땐 redux등의 외부 상태관리 라이브러리를 사용하는 경우가 많음. ㅈㄴ어렵다는 소리 ㅠㅠ

 

 

jsx는 자바스크립트 확장 문법임. jsx언어는 자바스크립트와 연관이 있으며, 자바스크립트의 문법을 확장시킨것.

jsx = 자바스크립트 + XML/HTML임.

대충 보자면

const element = <h1>Hello world</h1>;

이렇게 자바스크립트와 html코드가 결합되어있음.

이 위에 코드가 하는 역할은 element라는 변수에 h1태그로 둘러싸인 문자열을 저장하는것.

 

jsx는 내부적으로 html코드를 자바스크립트로 변환하는 과정을 거치게됨. 그래서 jsx코드를 작성해서 최종적으로는 자바스크립트 코드가 나오게 되는것.

이때 jsx코드를 자바스크립트 코드로 변환하는 역할을 하는 것이 바로 리액트의 createElement()라는 함수.

 

대충 예시를 들어보자면 

class Hello extend React.Component {
	render() {
    	return <div>Hello {this.props.toWhat}</div>
       }
 }
 
 ReactDOM.render(
 	<Hello toWhat="World" />
    document.getElementById('root')
 );

이건 Hello 라는 리액트 컴포넌트인데 jsx를 사용하고 있는걸 볼 수 있다.

그리고 ReactDOM의 render을 사용하여서 실제 화면에 렌더링하고있다.

 

class Hello extends React.Component {
	render() {
    	return React.createElement('div' , null, `Hello ${this.props.toWhat}`);
     }
}

ReactDOM.render(
	React.createElement(Hello, { toWhat: 'World' }, null),
    document.getElementById('root')
);

이 코드는 jsx를 사용하지않은 순수한 자바스크립트로만 작성된 코드이다 하지만 위코드와 아까 코드는 동일한 역할을 수행함.

jsx를 사용했던 부분이 React.createElement()라는 함수로 대체된걸 볼 수 있음!

---> jsx문법을 사용하면 리액트에서는 내부적으로 createElement()라는 함수로 사용하도록 변환됨.

 

그렇다면!! createElement()함수의 파라미터로는 어떤것이 들어가는지 자세히 살펴보자!!

React.createElement(
	type,
    [props],
    [...children]
)

왜 줄이 안맞지...흠...암튼

첫번째 파라미터로는 엘리먼트의 유형을 나타냄(type) 이 유형으로는 div나 span같은것들, 다른 리액트 컴포넌트같은 것들 있잖아 그런것들이 올 수 있음.

두번째 파라미터로는 props가 들어가게됨. 

세번째로는 children이 들어가게되는데 이 children이란 현재 엘리먼트가 포함하고 있는 자식 엘리먼트.

props란 개념은 추후에 다룰것임! 겁나게 어렵기 때문!

 

이렇게 jsx를 사용하면 createElement를 사용했을때보다 좀 더 간결하고 가독성이 올라가기에 jsx를 사용하는 것을 추천.

그리고 jsx를 사용하면 injection Attack이라 불리는 해킹을 방어할 수 있어서 보안성이 올라감.

const title = response.potentiallyMaliciousInput
const element = <h1>{title}</h1>
//이 코드는 안전함

이렇게 title이라는 변수에 잠재적으로 보안 위험의 가능성이 있는 코드가 삽입되었지만 jsx코드에서는 괄호를 사용하여 title변수를 삽입하고있음.

-> ReactDOM은 렌더링 하기 전에 임베딩(삽입)된 값을 모두 문자열로 변환하여서 명시적으로 선언되지 않은 값은 괄호 사이에 들어갈수없음( 만약 입력창에 문자나 숫자같은 일반적인 입력값이 아닌 소스코드를 입력하여서 해당 코드가 실행이되어버리면 해킹이 될수 있으나 이러한 특징이있어 방어가능)

결과적으로XSS라 불리는 cross-site-scripting attacks방어가능

그냥 좀 뭔가 심심하기도하고 그래서 딴소리좀 했움 ㅎㅎ...

 

jsx를 사용할땐 그냥 html과 자바스크립트가 섞인 형태로 코드를 작성해주면됨.

function formatName(user) {
	return user.firstName + ' ' + user.lastName;
 }
 
 const user = {
 	firstName: 'taeyoung',
    lastName: 'kim'
};

const element = (
	<h1>Hello, {formatName(user)}</h1>
);

ReactDOM.render(
	element,
    document.getElementById('root')
);

이렇게 html 코드 사이에 괄호를 사용해서 변수가 아닌 formatName(user)라는 자바스크립트 함수를 호출.

 

만약 사용자의 이름에 따른 인사말을 표현하고 싶다면?

function greeting(user) {
	if (user) {
    	return <h1> Hello, {formatName(user)}!</h1>
     }
     return <h1>Hi stranger</h1>
}

 

그럼 children을 정의하고싶다면?

그냥 하위태그를 상위태그로 감싼다고 생각하면됨.

const element = (
	<div>
      <h1>안녕</h1>
      <h2>리액트 공부하쟈</h2>
    </div>
 );

이렇게 하면 div태그의 children은 h1과 h2가 됨.

 

 

엘리먼트는 리액트 앱을 구성하는 요소를 의미함. 리액트 공식 홈페이지에서는 엘리먼트는 리액트 앱의 가장 작은 빌딩 블록들이라고 정의하고있음.

엘리먼트는 원래 웹사이트에 대한 모든 정보를 담고있는 객체인 DOM에서 사용하는 용어임.

하지만 DOM엘리먼트와는 다르게 리액트 엘리먼트가 따로있음.

DOM엘리먼트는 웹에서 개발자도구 켜서 나오는것들.

실제 브라우저의 DOM에 존재하는 엘리먼트는 DOM엘리먼트가 되는 것이고 리액트의 Virtual DOM에 존재하는 엘리먼트가 리액트 엘리먼트가 되는것임

즉, 리액트 엘리먼트는 DOM 엘리먼트의 가상 표현

엘리먼트는 컴포넌트 유형 과 속성 및 내부의 모든 자식에 대한 정보를 포함하고있는 일반적인 자바스크립트 객체.

 

이러한 엘리먼트는 불변성이라는 특징을 가지고있는데 엘리먼트는 한번 생성이되면 바꿀수 없기때문에 엘리먼트를 업데이트 하기 위해서는 다시 생성해야한다.

function tick() {
	const element = (
    	<div>
        	<h1>안녕 리액트!</h1>
            <h2> 현재 시간: {new Date().toLocaleTimeString()}</h2>
         </div>
     );
     
     ReactDOM.render(element, document.getElementById('root'));
    
 }
 setInterval(tick, 1000);

이 tick이라는 함수를 정의하고있는 코드는 현재 시간을 포함하고 있는 엘리먼트를 생성하여 root div에 렌더링하는 역할을 함. 

또한 자바스크립트의 setInterval()함수를 사용하여서 tick()함수를 1초에 한번씩 호출하고있음. 

이 코드의 실행결과는 매초 화면에 새로운 시간이 나오는데 이것은 tick함수가 호출될때마다 기존 엘리먼트를 변경하는것이 아니라, 새로운 엘리먼트를 생성하여 바꿔치기 하는것임.

 

근데 여기서 실제로 현재 시간을 초단위로 띄우는 걸 웹으로 표현할때 

 

이렇게 toLocaleTimeString()함수를 사용하여서 실제 시간을 받아온후에 그냥 index.js에 컴포넌트를 가져와서 화면에 나타내기만 하면 되는게 아니라 index에서도 추가작업이 필요함.

초당 새로 업데이트 되는 걸 표현해주기 위하여 

이렇게 setInterval함수를 사용하여서 매초마다 주기적으로 업데이트가 진행이 될수있도록 해주어야함.

 

리액트는 컴포넌트 기반의 구조임!

모든 페이지가 컴포넌트로 구성되어있고 하나의 컴포넌트는 또 다른 여러개의 컴포넌트의 조합으로 구성

이렇게 컴포넌트를 반복적으로 사용함으로써 전체 코드의 양이 줄어 자연스레 개발시간과 유지보수 비용도 줄일수있음.

 

이러한 리액트 컴포넌트에서의 입력은 props라는 것이고 출력은 앞에서 배운 리액트 엘리먼트가됨

결국 리액트 컴포넌트가 해주는 역할은 어떠한 속성들을 입력으로 받아서 그에 맞는 리액트 엘리먼트를 생성하여 리턴해주는것.

쉽게 말하자면 리액트 컴포넌트는 만들고자하는대로 props(속성)을 넣으면 해당 속성에 맞춰 화면에 나타날 엘리먼트를 만들어주는 것.

더 쉽게말하자면 붕어빵 틀 같은것임 여기서 붕어빵 틀은 컴포넌트, 각 붕어빵들은 엘리먼트

객체지향의 개념에서 나오는 클래스와 인스턴스의 개념과 비슷.

 

props는 리액트 컴포넌트의 속성임. 붕어빵을 만들때 컴포넌트라는 붕어빵 틀에 어떤 props를 넣는지에 따라 각기 다른 엘리먼트들이 나오는것임.

이처럼 props는 리액트 컴포넌트에서 눈에보이는 글자나 색 등의 속성을 바꾸고 싶을때 사용하는 컴포넌트속의 재료.

props는 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체

이러한 props를 통하여 컴포넌트를 통해 엘리먼트를 생성하면 이 생성된 엘리먼트에 대해서는 더이상 props값을 변경할수가없음. 따라서 다른 props값을 넣어주고싶다면 다시 컴포넌트에 값을 넣어서 새로운 엘리먼트를 만들어주면댐.

 

우리가 함수를 구분지을때에는 pure함수와 impure함수로 구분을 하는데 이때 

pure함수에 대해서 먼저 설명을 해보자면 

function sum(a, b) {
	return a+b;
}

이러한 간단한 함수를 예시로 들어보자면 a와b라는 파라미터의 값을 받아서 그 둘의 합을 리턴할때 a,b라는 파라미터의 값을 변경하지않고있음 -> 이경우가 pure하다.

 

impure함수를 보면

function withdraw(account, amount) {
	account.total -= amount;
}

이 함수를 보자면 account와 amount라는 파라미터를 받아서 account의 total이라는 값에서 amount라는 값을 빼는 함수임 쉽게 말하자면 은행 계좌에서 출금을 하는 함수. 이 경우를 보면 입력으로 받은 파라미터인 account의 값을 변경했으므로 impure한것임.

 

왜 이얘기를 했냐하면

리액트 공문에 모든 리액트 컴포넌트는 그들의 props에 관해서는 pure 함수 같은 역할을 해야함.

더 쉽게말하자면 리액트 컴포넌트는 props를 직접 바꿀순없고 같은 props에 관해서는 항상 같은 결과를 보여줄것!

 

jsx를 사용하면 키-값 쌍의 형태로 컴포넌트에 props를 넣을수있음

function App(props) {
	return (
    	<Profile
        	name="태영"
            introduction="안녕 난 태영이야"
            viewCount={1500}
         />
       );
 }

띄어쓰기 자꾸 왜이러는데...

App 컴포넌트가 나오는데 이 안에서 profile컴포넌트를 사용하고있음

이 profile컴포넌트 안에 name, introduction, viewCount라는 세가지 속성이 있는데 이 속성의 값이 모두 profile컴포넌트에 props로 전달되며, props는 아래와 같은 형태의 자바스크립트 객체가됨.

{
	name:"태영",
    introduction: "안녕 난 태영이야",
    viewCount: 1500
}

 주의할점은 props에 값을 넣을때에 문자열 이외에 정수, 변수, 그리고 다른 컴포넌트등이 들어갈 경우에는 중괄호를 사용하여서 감싸주어야함.

 

중괄호를 사용하는 예시를 들어보자면

funtion App(props) {
	return (
    	<Layout
        	width={2560}
            height={1440}
            header={
            	<Header title="태영의 블로그" />
             }
             footer={
             	<Footer />
             }
          />
       );
  }

 

컴포넌트는 함수 컴포넌트와 클래스 컴포넌트가 있음.

 

컴포넌트의 이름을 지을땐 항상 대문자로 시작.

소문자로 시작하면 DOM태그로 인식하기때문임.

 

이러한 컴포넌트들을 당연히 합성을 해서 만들수도있음.

function Welcome(props) {
	return <h1>Hello, {props.name}</h1>
 }
 
 function App(props) {
 	return (
    	<div>
        	<Welcome name="Mike" />
            <Welcome name="Steve" />
            <Welcome name="Jane" />
         </div>
      )
  }
  
  ReactDOM.render(
  	<App />
    document.getElementById('root')
);

이렇게 해버리면 App컴포넌트는 Welcome컴포넌트 세개를 포함하고있는 컴포넌트가됨.

 

반대로 복잡한 컴포넌트를 쪼개서 컴포넌트 추출을 할수도있음.

이걸 잘하면 컴포넌트의 재사용성이 올라감.

 

자자 가보자~

function Commnet(props) {
	return (
    	<div className="comment">
        	<div className="user-info">
            	<img className="avatar">
                	src={props.author.avatarUrl}
                    alt={props.author.name}
                  />
                  <div className="user-info-name">
                  	{props.author.name}
                  </div>
                </div>
                
                
                <div className="comment-text">
                	{props.text}
                 </div>
                 
                 
                 <div className="comment-data">
                 	{formatDate(props.date)}
                 </div>
             );
      }

여기에 이렇게 comment컴포넌트가 있는데 이건 내부에 작성자의 프로필 이미지,  이름, 댓글내용 ,작성일이 들어가있음.

이 컴포넌트에서 하나씩 컴포넌트들을 추출해보자.

 

우선 avatar컴포넌트 추출임.

comment 컴포넌트에서는 이미지태그를 사용하여서 사용자의 프로필 이미지를 표시하고 있음. 이부분을 추출할거임

 

function Avatar(props) {
	return (
    	<img className="avatar"
        	src={props.user.avartarUrl}
            alt={props.user.name}
          />
       );
  }

props에 기존에 있던 author보다 좀 더 보편적인 user사용. 이렇게 보편적인것을 사용해야 재사용성이 올라가기때문.

이제 이 추출한 컴포넌트를 다시 위에있는 코드에 적용한다면?

 

function Commnet(props) {
	return (
    	<div className="comment">
        	<div className="user-info">
            	<Avatar user={props.author} />
                  <div className="user-info-name">
                  	{props.author.name}
                  </div>
                </div>
                
                
                <div className="comment-text">
                	{props.text}
                 </div>
                 
                 
                 <div className="comment-data">
                 	{formatDate(props.date)}
                 </div>
             );
      }

이렇게됨.

이제 다음으로는 사용자 정보를 담고있는 부분을 추출해보자.

 

function UserInfo(props) {
	return (
    	<div className="user-info">
        	<Avatar user={props.user}>
            <div className="user-info-name">
            	{props.user.name}
            </div>
        </div>
      );
  }

사용자 정보를 담고있는 부분을 추출할때 이렇게 avatar컴포넌트도 같이 안에 들어가있는걸 볼 수 있다.

역시 아까와 마찬가지로 좀 더 보편적인 의미를 담고있는 user을 props에 사용

 

그럼 이 추출한 UserInfo 컴포넌트도 원래의 comment컴포넌트에 대입을 해보자

function Commnet(props) {
	return (
    	<div className="comment">
        	<UserInfo user={props.author} />
            
                <div className="comment-text">
                	{props.text}
                 </div>
                 
                 
                 <div className="comment-data">
                 	{formatDate(props.date)}
                 </div>
             );
      }

전보다 훨씬 간단해진걸 볼 수 있다.

이러한 컴포넌트 추출을 함에 있어서 기능단위로구분하는것이 좋고, 나중에 곧바로 재사용이 가능한 형태로 추출하는것이 좋다.

 

이렇게 comment 컴포넌트를 만들었는데 comment의 작성자 이름과 댓글 내용을 동적으로 변경해주기 위하여 props를 사용함.

근데 이렇게 하고 npm run start를 하면 작성자 이름과 댓글 내용이 나오지 않는데 그 이유는 그냥 name이랑 comment값이 정의가 안되어있어서 그럼 css는 걍 알아서 먹임

 

이런식으로 컴포넌트에 props값을 전달해주면

이렇게 뜸.

 

그럼 이제 데이터를 별도의 객체로 분리해서 동적으로 받아온 데이터를 표시가되게 만들어보자

왜냐면 저런식으로하면 어떻게 데이터를 받아와서 일일이 다 저렇게해 ㅇㅈ?

 

이렇게 예시로 더미데이터를 만들어서 이걸 map함수에 적용해서 comments에 있는 데이터들을 받아와서 컴포넌트에 대입시켜서 출력해주면댐.

 

 

'REACT' 카테고리의 다른 글

Life cycle and hook  (0) 2023.07.14
Shared state  (0) 2023.07.13
REACT  (0) 2023.04.29
React  (0) 2022.11.09
useRef  (0) 2022.07.28

MPA / SPA

MPA(Multi-page-application)는 여러 페이지로 이루어진 어플리케이션을 말합니다 즉 mpa는 사용자가 다양한 페이지로이동하면서 애플리케이션을 사용하는 전통적인 방식입니다. 반면에 리액트는 SPA(single-page application)를 지향합니다 SPA는 한개의 페이지로 이루어진 어플리케이션으로 새로운 페이지를 로딩할 필요없이 화면이 동적으로 바뀌는 것을 특징으로 합니다. SPA는 사용자 경험과 속도 측면에서 우수한 성능을 보여주기때문에 최근에는 많은 웹 어플리케이션이 SPA로 구현됩니다.

 

컴포넌트

클래스형 컴포넌트

 

리액트에서 클래스형 컴포넌트는 ES6의 클래스 문법을 사용하여 정의되는 컴포넌트 입니다. 클래스형 컴포넌트는 React.Component클래스를 상속받아서 만들어지며 render()메소드를 포함하고 있습니다.

다음 코드는 React.Component클래스를 상속받아 MyComponent라는 클래스형 컴포넌트를 정의합니다.

import React from ‘react’;

class MyComponent extends React.Component {

render() {
return <h1>Hello, World</h1>;
}
}

클래스형 컴포넌트는 다음과 같은 특징이 있습니다.

this.props를 사용하여 부모컴포넌트로부터 전달된 속성값에 접근할 수 있습니다.

state객체를 사용하여 컴포넌트 내부 상태를 관리할 수 있습니다.

this.setState()메소드를 사용하여 state를 업데이트 할 수 있으며

state가 변경될때마다 render()메소드가 다시 호출됩니다.

 

함수형 컴포넌트

 

함수형 컴포넌트는 리액트에서 컴포넌트를 정의하는 또 다른 방법입니다. 함수형 컴포넌트는 단순한 자바스크립트 함수로 정의되며, 입력값을 받아들여 UI를 반환합니다.

예를들어,

import React from ‘react’;

function MyComponent(props) {
return <h1>Hello, {props.name}!</h1>;
}

이코드는 함수형 컴포넌트입니다.

props를 사용하여 부모 컴포넌트로부터 전달된 속성값에 접근할수있습니다.

useState()훅을 사용하여 컴포넌트의 내부 상태를 관리할 수 있습니다.

useEffect()훅을 사용하여 컴포넌트가 생성되거나 업데이트될때 필요한 작업을 수행할 수 있습니다.

함수형 컴포넌트는 클래스형 컴포넌트보다 더 간결하고 가독성이 높으며, 성능면에서도 우수합니다.

 

JSX 문법

 

javascript의 확장 문법으로 리액트에서 UI를 정의하는데 사용됩니다.

JSX는 javascript코드 내부에 XML형태로 작성되며, XML과 유사한 문법을 사용하여 UI를 표현합니다.

const element = <h1>Hello,world!</h1>;

이 코드는 jsx를 사용하여 h1요소를 렌더링합니다.

html과 비슷한 문법을 사용하여 리액트 어플리케이션을 작성할수 있습니다.

자바스크립트 식을 {}중괄호로 둘러싸서 jsx내부에 포함시킬수있습니다.(이를통해 동적인 ui생성가능)

jsx에서는 클래스 이름 대신 className속성을 사용합니다(jsx가 html이 아닌 자바스크립트이기 때문에)

 

Props / State

props:

props는 컴포넌트간에 데이터를 전달하는데 사용됩니다. 부모 컴포넌트가 자식 컴포넌트에게 속성값을 전달하면 자식 컴포넌트에서는 props를 통해 전달받은 값을 사용할 수 있습니다.

function MyComponent(props){

return <h1>hello {props.name}</h1>;

}

ReactDOM.render(<MyComponent name=”taeyoung” />, document.getElementById(’root’));

 

이 코드는 name이라는 props를 전달하고있습니다.

MyComponent 컴포넌트에서 props 객체를 받아서 {props.name}으로 전달된 name속성값을 출력하고 있습니다.

 

state:

state는 컴포넌트의 내부 상태를 관리하는데 사용됩니다. state를 사용하면 컴포넌트가 렌더링될때 동적인 ui를 생성할수있습니다.

state는 클래스형 컴포넌트에서 this.state를 통해 접근하고 this.setState()를 사용하여 업데이트 할 수 있습니다.

class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>You clicked {this.state.count} times.</p>
<button onClick={() => this.handleClick()}>Click me</button>
</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById('root'));

MyComponent클래스형 컴포넌트에서 state객체를 생성하고, count 값을 0으로 초기화합니다. handleClick()메서드를 사용하여 count값을 증가시키고, this.setState()를 호출하여 state객체를 업데이트합니다. render()메서드에서는 this.state.count값을 출력하고, 버튼 클릭시 handleClick()메서드를 호출합니다.

 

Virtual DOM

virtual DOM은 브라우저의 실제 DOM과 유사한 트리구조로 이루어진 객체입니다 리액트는 상태가 변경될때마다 가상의 DOM을 생성하고 이전 가상 DOM과 비교하여 변경된 부분만 실제 DOM에 반영합니다.

이것은 DOM조작이 일어날때마다 브라우저가 전체 페이지를 다시 그리는 것을 방지하여 성능을 향상시킵니다 또한 리액트는 가상 DOM의 변경사항을 최적화하여 실제 DOM조작을 최소화하도록 합니다.

 

Reconciliation

RECONCILIATION는 리액트에서 가상 DOM을 사용하여 업데이트를 최적화하는 과정입니다. 리액트는 상태가 변경될때마다 새로운 가상 DOM을 만들고 이전 가상 DOM과 비교하여 변경된 부분만 실제 DOM에 반영합니다 이 과정에서 RECONCILIATION이 발생합니다.

Reconciliation은 리액트에서 성능 최적화를 위한 중요한 과정 중 하나입니다. 이를 통해 불필요한 렌더링 작업을 방지하고, 빠르고 효율적인 UI 렌더링을 달성할 수 있습니다.

 

Event handling

이벤트 핸들링은 웹 어플리케이션에서 사용자와 상호작용할 수 있는 방법중 하나입니다. 이는 사용자의 입력에 대한 응답으로 코드를 실행하는것을 의미합니다.

버튼클릭, 마우스이동, 키보드입력등의 이벤트 처리를 할수있습니다.

리액트에서 이벤트 처리는 일반적으로 JSX문법을 사용하여 처리됩니다. JSX에서는 이벤트처리기를 props로 전달하여 구성요소에서 이벤트를 처리합니다.

import React from 'react';

function Button() {
function handleClick() {
console.log('Button clicked');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}

이런식으로 버튼을 클릭할대마다 함수를 실행시킬수있습니다.

onClick prop을 사용하여 클릭 이벤트에 대한 처리기 함수인 handleClick을 전달합니다. 이벤트 처리기는 함수로 전달되며, 이벤트가 발생할 때마다 호출됩니다.

'REACT' 카테고리의 다른 글

Shared state  (0) 2023.07.13
React Foundation  (0) 2023.06.27
React  (0) 2022.11.09
useRef  (0) 2022.07.28
React virtual DOM  (0) 2022.07.28

리액트 프로젝트를 실시할때에는 우선 node.js를 깐 후에 리액트 프로젝트를 실시할수있는 폴더를 만든후에 그 폴더에서 git bash를 통해 vs code를 연다 그 후에 보기에서 터미널을 눌러 열어주고 터미널에 npm init react-app .이라고 쳐준다 그 후에 프로젝트 실행 명령어인 npm run start를 누르면 프로젝트가 웹을 통해 실행되는것을 볼수이떠 그리고 웹을 끄려면 ctrl+c해주면댐

이렇게해서 보라는 버튼을 클릭했을때 곧 도착합니다 라는 경고문을 띄우게 만들수있음 굳굳!!

'REACT' 카테고리의 다른 글

React Foundation  (0) 2023.06.27
REACT  (0) 2023.04.29
useRef  (0) 2022.07.28
React virtual DOM  (0) 2022.07.28
jsx문법  (0) 2022.07.21

+ Recent posts