7 minute read

리액트의 템플릿 파일

.jsx / .tsx 확장자

리액트는 템플릿 파일을 사용하지 않는다.

리액트에서 사용하는 파일 (템플릿파일이 아니다^^)

  • JSX 파일
    리액트의 기본 파일 형식. JSX는 JavaScript의 확장 문법으로 JavaScript 파일 내에서 XML과 유사한 형태로 컴포넌트의 UI를 작성한다. 이 파일은 일반적으로 “.jsx” 또는 “.js” 확장자를 가진다.
  • TSX 파일(선택)
    TypeScript와 JSX를 함께 사용하는 경우에 사용되는 파일 형식. TypeScript는 정적 타입 체크와 다양한 기능을 제공하므로, TSX 파일은 TypeScript를 사용하여 작성된 리액트 컴포넌트에서 JSX 문법을 사용할 때 사용한다. 이 파일은 일반적으로 “.tsx” 또는 “.ts” 확장자를 가진다.

    JSX 파일에서 추가 기능으로 TypeScript를 사용하는 경우에는 TSX 파일을 사용하여 리액트 컴포넌트를 작성한다.

요약하면 리액트는 JSX(JavaScript XML)라는 독자적인 템플릿 문법을 가진 파일을 사용한다. 주로 자바스크립트 안에 JSX 문법으로 컴포넌트를 작성하게 된다. 이는 XML과 유사한 문법이다. 이렇게 작성된 JSX 코드는 자바스크립트 코드로 변환되어 브라우저에서 실행된다. 따라서 리액트 애플리케이션의 모든 코드는 일반적인 자바스크립트 파일(.js) 형태로 작성된다.

.JSX 파일에서만 JSX 문법이 사용 가능할까?

리액트는 JSX 문법을 사용하는 파일의 확장자는 반드시 .jsx 일 필요는 없다.

원칙대로는 JSX 문법이 포함된 파일을 사용하려면 필요한 도구를 설치 및 구성해야 한다. 그런데 최신 버전의 React에서는 .js 확장자를 사용하는 파일에서도 JSX 문법을 사용할 수 있도록 Babel 설정이 자동으로 포함되어 있다. 그러나 일반적으로 JSX를 사용하는 파일에는 .jsx 확장자를 사용하는 것이 좋다. 이는 코드의 가독성을 높이고 개발자들 사이에서 일관성을 유지하는 데 도움이 된다.

JSX 문법을 사용하는 파일(js)에서 최소필요조건

JSX 문법을 사용하기 위해서는 다음과 같은 최소 조건이 필요하다.

  1. Node.js가 설치되어 있어야 한다.
  2. npm 또는 yarn과 같은 패키지 매니저가 설치되어 있어야 한다.
  3. React가 설치되어 있어야 한다.
  4. 바벨(Babel)과 같은 트랜스파일러가 필요하다.
    • npm install @babel/core @babel/preset-env @babel/preset-react babel-loader --save-dev
      
      # 이후에는 JSX 문법을 사용하는 파일을 만들고, 해당 파일을 불러와 사용하면 된다.
      
      

그런데 요즘은 Babel 설치가 필요 없다고 한다.

현재 최신 버전에서는 바벨(Babel)을 사용하지 않아도 JSX 문법을 사용할 수 있다. Create React App 같은 도구를 사용하면 기본적으로 Babel 설정이 포함되어 있어 따로 설정할 필요가 없다고 한다. 하지만 기존 과거 버전 프로젝트에서 JSX를 사용하려면 Babel을 설치하고 설정해주어야 한다.

JSX 기본 사용법

루비테마 지킬 기반의 내 블로그안에 학습용 리액트를 연동하여 추가로 구축했다. (오랜만에 블로그 설정파일을 조작했당) JSX를 테스트하며 작성한 리액트프로젝트 화면은 아래 링크와 같다.

링크

https://git.blang.co.kr/blang-react-pages/

클래스

setState 객체 반환시

// 기본구조 prevState
<button onClick={ () => { this.setState( (prevState) => { return ({age: prevState.age + 1})   }  );  }}> 증가1(기본) </button>

// (O) prevState 객체 괄호로 감싸서 정상
<button onClick={ () => { this.setState( (prevState) => ({ age: prevState.age + 1})  );  }}> 증가2(prevState 함수형) </button>
			
// (X) prevState 객체 괄호로 감싸지 않아서 에러
<button onClick={ () => { this.setState( (prevState) => { age: prevState.age + 1}  );  }}> 증가3(문법오류) </button>

// 콜백추가 prevState
<button onClick={ () => { this.setState( (prevState) => ({ age: prevState.age + 1}), ()=> { alert("Hello");}  );  }}> 증가3(콜백) </button>

아래와 같이 작성해야한다.
setState() 메소드 안에서 객체를 반환할 때에는, 객체를 괄호로 감싸주어야 하며, 괄호가 누락될 경우 에러가 발생한다.

<button onClick={ () => { this.setState( (prevState) => ({ age: prevState.age + 1} ) ); }}> 증가2 </button>

리듀스(useReducer)

리듀스 사용법 (기본)

사진

import { useReducer, useState } from 'react';

function reducer1(state1, action1) {
	//action 별 분기처리
	switch (action1.type) {
		case 'INCREMENT' :
			return { value: state1.value +1 };
		case 'DECREMENT': 
			return { value: state1.value -1 };
		default: 
			//아무것도 해당되지 않을때 기존상태 반환
			return state1;
	}
}


const TestUseReducer = () => {
	
	const [state1, dispatch1] = useReducer( reducer1, {value1: 1});
	  
	return (
	    <div className='main-inner-list'>
			<h4> UseReducer 종류별 테스트</h4>
			<hr />
			<h4> UseReducer 으로 카운터 구현하기</h4>
			<p> 현재 카운터 값은 <b>{state1.value1}</b> 입니다. </p>
			
			<button onClick={ () => dispatch1( { type: 'INCREMENT' } ) }>[ + 1 ]</button>
			<button onClick={ () => dispatch1( { type: 'DECREMENT' } ) }>[ - 1 ]</button>
			
	    </div>
	);
};

export default TestUseReducer;

  1. onClick 을 통한 액션이벤트별 +,- 로직 수행
    • useState 갱신

리듀스 사용법 (여러 인풋받기)

사진1

import { useReducer, useState } from 'react';

function reducer2(state2, action2) {
	// 객체형태를 리턴한다. 
	return { 
		...state2,
		[action2.name] : action2.value 
	};
}

// 리턴함수
const TestUseReducer = () => {
	
	const [state2, dispatch2] = useReducer( reducer2, 
		{ name: '',  
	    nickname: '' }
	);
    
  const { name, nickname } = state2;
 
  const onChange = e => {
  	dispatch2(e.target);
  }
	
	return (
	    <div className='main-inner-list'>
			<h4> UseReducer 으로 인풋상태 관리하기</h4>
			<div>
				<input name="name" value={name} onChange={onChange} />
				<input name="nickname" value={nickname} onChange={onChange} />
			</div>
			<div> <b>이름: </b> {name} </div>
			<div> <b>닉네임: </b> {nickname} </div>
	    </div>
	);
};
export default TestUseReducer;

  1. onChange 를 통한 값을 입력받기
    • 입력받은 값을 즉시 [리듀스] 정의를 찾음. (기존 useState 저장하는 형태X) 입력받은 값을 즉시 [리듀스의 액션함수] 를 찾음. (기존 useState 저장하는 형태X)
  2. 정의된 [리듀스의 액션함수] 수행
    • 전개연산자로 기존 내용을 생성하고
    • 입력받은 액션 함수 내용을 추가로 저장

메모(useMemo)

useMemo 사용법(평균계산기 AS-IS)

import { useReducer, useState } from 'react';

const getAverage = (numbers) => {
    console.log('평균값 계산중...');
    if ( numbers.length === 0 ) return 0;
    const sum = numbers.reduce( (a, b) => a + b );  //파라미터1: 배열, 파라미터2: 반환값을 다시누산
    return sum / numbers.length;
};

// 리턴함수
const TestUseReducer = () => {
	
    const [list, setList] = useState( [] );
    const [number, setNumber] = useState('');

    const onChangeNum = (e) => {
        setNumber(e.target.value);
    };
        
    const onInsert = (e) => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };

    return (
        <div className='main-inner-list'>
        <h4> UseReducer 종류별 테스트</h4>
        <hr />
            <h4> UseMemo 으로 연산(평균) 최적화하기</h4>
            <input value={number} onChange={onChangeNum} />
            <button onClick={onInsert}>등록</button>
            <ul>
                {list.map((value, index) => (
                <li key={index}>{value}</li>
                ))}
            </ul>
            <div>
            <b>평균값:</b> {getAverage(list)}
            </div>
        			
        </div>
    );
};

export default TestUseReducer;

이 방식은 등록 버튼을 클릭하지 않아도 인풋 내용이 수정이 되면 불필요하게 계속 렌더링된다.

  1. onChange 를 통한 값을 입력받기
    • 입력전용 useState 저장
  1. onInsert 를 통한 입력값을 배열에 추가하기
    • concat 으로 기존값 입력값을 호출.
    • 리스트전용 useState 저장
    • 입력전용 useState 빈값으로 초기화
  2. map() 함수를 통한 저장된 리스트 내용을 화면에 SET
    • 리스트전용 useState 에서 map 함수로 반복하여 꺼내기

useMemo 사용법(평균계산기 TO-BE)

import { useState, useMemo } from 'react';

const getAverage = (numbers) => {
    console.log('평균값 계산중...');
    if ( numbers.length === 0 ) return 0;
    const sum = numbers.reduce( (a, b) => a + b );  //파라미터1: 배열, 파라미터2: 반환값을 다시누산
    return sum / numbers.length;
};

// 리턴함수
const TestUseReducer = () => {
	
    const [list, setList] = useState( [] );
    const [number, setNumber] = useState('');

    const onChangeNum = (e) => {
        setNumber(e.target.value);
    };
        
    const onInsert = (e) => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };
  
    const avg = useMemo(() => getAverage(list), [list]);
  
	return (
	    <div className='main-inner-list'>
			<h4> UseMemo 으로 연산(평균) 최적화하기</h4>
            <input value={number} onChange={onChangeNum} />
            <button onClick={onInsert}>등록</button>
            <ul>
              {list.map((value, index) => (
                <li key={index}>{value}</li>
              ))}
            </ul>
            <div>
              {/* <b>평균값:</b> {getAverage(list)} */}
              <b>평균값:</b> {avg}
            </div>
			
	    </div>
	);
}

export default TestUseReducer;

  • avg 가 추가되었다.
  • 렌더링하는 과정에서 특정값값이 바뀐 경우에만 연산을 싱행하는 기능이다.
  • 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 재사용한다.

콜백(useCallback)

useCallback 사용법

import { useMemo, useCallback, useState } from 'react';

const getAverage = (numbers) => {
    console.log('평균값 계산중...');
    if ( numbers.length === 0 ) return 0;
    const sum = numbers.reduce( (a, b) => a + b );  //파라미터1: 배열, 파라미터2: 반환값을 다시누산
    return sum / numbers.length;
};


// 리턴함수
const TestUseCallback = () => {
    const [list, setList] = useState([]);
    const [number, setNumber] = useState('');

		// onChagne 이벤트
    const onChangeNum = useCallback(e => {
        setNumber(e.target.value);
      
    }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
        

    // onInsert 이벤트
    const onInsert = useCallback(() => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    }, [number, list]); // number 혹은 list 가 바뀌었을 때만 함수 생성


    const avg = useMemo(() => getAverage(list), [list]);
  
	return (
	    <div className='main-inner-list'>
			
			<h4> UseMemo 으로 연산(평균) 최적화하기 AS-IS</h4>
            <input value={number} onChange={onChangeNum} />
            <button onClick={onInsert}>등록</button>
            <ul>
              {list.map((value, index) => (
                <li key={index}>{value}</li>
              ))}
            </ul>
            <div>
              <b>평균값:</b> {getAverage(list)}
            </div>
			
			<hr />
			<h4> UseMemo 으로 연산(평균) 최적화하기 TO-BE</h4>
            <input value={number} onChange={onChangeNum} />
            <button onClick={onInsert}>등록</button>
            <ul>
              {list.map((value, index) => (
                <li key={index}>{value}</li>
              ))}
            </ul>
            <div>
              <b>평균값:</b> {avg}
            </div>			
			
	    </div>
	);
};

export default TestUseCallback;

  • usecallback의 첫 번째 파라미터에는 생성하고 싶은 함수를 봉고. 두 번째 파라미터에는 배열을 넣으면 됩니다.

  • 이 배열에는 어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시해야 합니다.
    • onchange처럼 비어 있는 배열을 넣게 되면 컴포넌트가 렌더링될 때 만들었던 함수를 계속해서 재사용하게 되며 lnsert 처럼 배얼 안에 number와 1ist를 넣게 되면 인풋 내용이 바뀌거나 새로운 항목이 추가될 때 새로 만들어진 함수를 사용하게 됩니다.
  • 함수 내부에서 상태 값에 의존해야 할 때는 그 값을 반드시 두 번째 파라미터 안에 포함시켜 주어야합니다.
    • 예를 들어 onchange의 경우 기존의 값을 조회하지 않고 바로 설정만 하기 때분에 배열이 비어 있어도 상관없지만. onInsert는 기존의 number와 list를 조회해서 nextlist를 생성하기 때문에 배열 안에 number와 list를 꼭 넣어 주어야 합니다.

useRef

useRef 사용법

import React, { useState, useMemo, useRef, useCallback } from 'react';

const getAverage = numbers => {
  console.log('평균값 계산중..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};


const Average = () => {

  // 예제1) ref 엘리먼트지정
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');
  const inputEl = useRef(null);

  const onChange = useCallback(e => {
    setNumber(e.target.value);
    
  }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성

  const onInsert = useCallback(() => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
    inputEl.current.focus();
    
  }, [number, list]); // number 혹은 list 가 바뀌었을 때만 함수 생성

  const avg = useMemo(() => getAverage(list), [list]);


  // 예제2) ref 로컬변수에도 가능하다.
  const RefSample = () => {
    const id = useRef(1);
    const setId = (n) => {
      id.current = n;
    }
    const printId = () => 
    console.log(id.current);
  }

  return (
    <div className='main-inner-list'>
       <input value={number} onChange={onChange} ref={inputEl} />
       <button onClick={onInsert}>등록</button>
       <ul>
          {list.map((value, index) => (
             <li key={index}>{value}</li>
           ))}
       </ul>
          
        <div>
          <b>[ref]평균값:</b> {avg}
        </div>
      
        <div>
          RefSample: { RefSample } 
        </div>
     </div>
);
};

export default Average;

Tags:

Categories:

Updated:

Leave a comment