리스트(List)

:같은 아이템을 순서대로 모아놓은 것

 

키(Key)

:각 객체나 아이템을 구분할 수 있는 고유한 값(두 리스트 사이에선 키가 같아도 상관없음)

 

Array.map() 활용하여 여러개의 컴포넌트 랜더링

: 배열에 모든 요소에 대해 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환

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

 

 

리스트 출력하는 컴포넌트

./components/NumberList.jsx

import React from "react";
export default function NumberList(props) {
const { numbers } = props;
const listItems = numbers.map( number =>
<li>{number}</li>
);
return (
<ul> {listItems} </ul>
);
}

 

./index.js

import NumberList from './components/NumberList'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<NumberList numbers={ [1, 2, 3, 4, 5] } />
</React.StrictMode>
);

 

 

*실행시 경고문이 출력되는데 , 현재 각 아이템에 키가 없기 때문임

 

키 값으로 숫자의 값을 사용

const numbers = [1,2,3,4,5];//배열 요소 중복 가능서 있음
const listItems = numbers.map(number =>
<li> key = {numbber.toString()}>{number}</li>
);

 

키 값으로 id를 사용하는 방식(아이템의 고유한 id가 있을 경우 사용)

const todos = [
{id:1, text: '첫 번째 할일'},
{id:2, text: '두 번쨰 할일'}
]
const todoItems = todos.map(todo =>
	<li key = {todo.id}>{todo.text}</li>
    );

 

 

키 값으로 인덱스를 사용하는 방법 - 아이템의 고유한 id가 없을 경우 사용

map() 메서드 안에 있는 엘리먼트는 key가 필수

const todos = [
{ text: '첫 번째 할일'},
{ text: '두 번쨰 할일'}
]
const todoItems = todos.map(todo, index) =>
	<li key = {index}>{todo.text}</li>
    );

 

 

실습) 출석부 만들기

import React from "react";

const students = [
   {
		id:1,
    	name: "Inje",
    },
    {	id:2,
    	name: "Steve",
    },
      {	id:3,
    	name: "Bill",
    },
    
];

export default function AttenanceBook(props){
	return(
		<ul>
			{students.map((student, index) => {
            	return<li key = {student.id}>
                	{student.name}</li>
             })}
             </ul>
         );
   }

 

 

폼(Forms)

:사용자로부터 입력을 받기 위해 사용하는 양식

 

리액트는 컴포넌트 내부에서 state를 통해 데이터를 관리

 

제어 컴포넌트(Controlled Component)

React에 의해서 값이 제어되는 컴포넌트 (input form element)

제어 컴포넌트를 사용하면 입력값이 리액트 컴포넌트의 state를 통해 관리할 수 있음

 사용자의 입력을 직접적으로 제어할 수 있음

 

function NameForm(props) {
	const[value, setValue] = useState('');
    
    const handleChange = (event) => {
    	setValue(event.target.value); //event객체->event.target: 이벤트가 발생한 타겟
        }

	const handleSubmit = (event_ => {
    	alert(`입력한 이름 : ${vlaue}`);
        event.preventDefault();
        }//event.preventDefault();->새로운 페이지로 연결되는 기본 동작 중단시킴
        
        return(
        <form onSubmit = {handleSubmit}>
        <label>
        이름:
        <input type = "text" value = {value} onChange = {handleChange} />
        </label>
        <button type = "submit" >제출 </button>
        </form>
        )
        }

 

 

textarea 태그

• 여러 줄의 텍스트를 입력 받기 위한 HTML 태그

• 리액트 – textarea 태그의 value 라는 속성을 사용하여 텍스트를 표시

<label>
요청사항:
<textarea value = {value} onChange = {handleChange}/>
</label>

 

 

select 태그

• drop-down 목록을 보여주기 위한 HTML 태그

리액트 – selected 대신 value 속성을 사용하여 선택 값을 표시

 

// FruitSelect라는 이름의 React 함수 컴포넌트를 정의합니다.
// 이 컴포넌트는 props라는 매개변수를 받지만, 현재 코드에서는 사용하지 않습니다.
function FruitSelect(props) {
	// useState 훅을 사용하여 'value'라는 상태 변수를 선언하고,
	// 초기값을 'grape' (포도)로 설정합니다.
	// value는 현재 선택된 과일의 값을 저장합니다.
	// setValue는 value 상태를 업데이트하는 함수입니다.
	const [value, setValue] = React.useState('grape'); // React.useState로 명시적으로 사용

	// select 박스의 값이 변경될 때 호출되는 함수입니다.
	const handleChange = (event) => {
    	// 이벤트 객체(event)를 통해 현재 선택된 select option의 value를 가져와서
    	// setValue 함수를 사용하여 value 상태를 업데이트합니다.
        // 오타가 있어서 event.targegt.value -> event.target.value 로 수정했습니다.
    	setValue(event.target.value);
    }

    // 폼 제출(submit) 이벤트가 발생했을 때 호출되는 함수입니다.
    // 오타가 있어서 handleSubit -> handleSubmit 로 수정했습니다.
    const handleSubmit = (event) => {
        // 현재 value 상태에 저장된 과일 값을 사용하여 경고창을 띄웁니다.
		alert(`선택한 과일: ${value}`);
        // 폼의 기본 제출 동작(페이지 새로고침 등)을 막습니다.
        event.preventDefault();
     }

    // 컴포넌트가 렌더링할 JSX(JavaScript XML)를 반환합니다.
    return(
    // 폼(form) 엘리먼트를 생성하고, 제출 이벤트(onSubmit)가 발생하면
    // 위에서 정의한 handleSubmit 함수를 호출하도록 설정합니다.
    <form onSubmit = {handleSubmit}>
    	{/* 레이블 엘리먼트를 생성합니다. */}
    	<label>
        과일을 선택하세요:
        	{/* select 엘리먼트를 생성합니다. */}
            {/* value 속성은 현재 select 박스에 표시될 값을 value 상태 변수와 연결합니다. */}
            {/* onChange 속성은 select 박스의 값이 변경될 때 handleChange 함수를 호출하도록 설정합니다. */}
        	<select value = {value} onChange = {handleChange}>
            	{/* 옵션 엘리먼트들입니다. 각각 과일의 값과 표시될 텍스트를 정의합니다. */}
            	<option value = "apple"> 사과</option>
                <option value = "banana"> 바나나</option>
                <option value = "grape"> 포도</option>
            </select> {/* select 엘리먼트 닫는 태그가 빠져있어서 추가했습니다. */}
        </label>
        {/* 제출(submit) 버튼을 생성합니다. */}
        <button type = "submit"> 제출</button>
    </form>
    )
}

// 이 컴포넌트를 다른 파일에서 사용할 수 있도록 내보냅니다. (예: export default FruitSelect;)
// 실제 사용 시에는 이 컴포넌트를 렌더링하는 코드가 필요합니다.
// 예: ReactDOM.render(<FruitSelect />, document.getElementById('root'));

 

• multiple inputs

• 여러 개의 state를 선언하여 각각의 입력에 대해 사용

 

Input Null Value

• 제어 컴포넌트에서 value 값을 지정하되 입력 값을 자유롭게 변경하고자 할 때

• 제어 컴포넌트에 value 값이 정해지면 입력값을 바꿀 수 없음

• value에 undefined 또는 null을 넣어 주면 입력이 가능한 상태로 변경할 수 있음

 

 

실습)) 회원가입 폼 만들기

./components/SignUpORg.jsx

import React, { useState } from "react";

// 컴포넌트 정의: 회원가입 폼
export default function SignUp() {
  // 각 입력 필드의 상태(state) 정의
  const [name, setName] = useState("");             // 이름
  const [gender, setGender] = useState("남자");     // 성별 (기본값: 남자)
  const [email, setEmail] = useState("");           // 이메일
  const [password, setPassword] = useState("");     // 비밀번호
  const [birthdate, setBirthdate] = useState("");   // 생년월일
  const [agree, setAgree] = useState(false);        // 약관 동의 체크 여부
  const [error, setError] = useState("");           // 에러 메시지 상태
  const [emailValid, setEmailValid] = useState(true); // 이메일 유효성 여부

  // 이메일 형식을 정규표현식으로 검사하는 함수
  const validateEmail = (email) => {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email); // 정규표현식에 맞으면 true, 아니면 false 반환
  };

  // 이메일 입력 시마다 호출되는 함수 (실시간 유효성 검사)
  const handleEmailChange = (e) => {
    const value = e.target.value;
    setEmail(value); // 입력된 이메일 값을 상태에 저장
    setEmailValid(validateEmail(value)); // 이메일 유효성 검사 결과 저장
  };

  // 폼 제출 시 실행되는 함수
  const handleSubmit = (event) => {
    event.preventDefault(); // 폼의 기본 제출 동작을 막음

    // 필수 입력 항목 확인
    if (!name) {
      alert("이름을 입력해주세요");
      return;
    }

    if (!validateEmail(email)) {
      alert("이메일 주소가 유효하지 않습니다");
      return;
    }

    if (!agree) {
      alert("약관에 동의해주세요");
      return;
    }

    // 모든 조건이 만족되었을 때 실행
    setError(""); // 에러 메시지 초기화
    alert(`이름: ${name}\n이메일: ${email}\n성별: ${gender}\n생년월일: ${birthdate}`);
  };

  // 실제로 사용자에게 보여지는 폼 구성
  return (
    <form onSubmit={handleSubmit}>
      <h2>회원가입</h2>

      {error && <p style={{ color: "red" }}>{error}</p>} {/* 에러 메시지 표시 */}

      <label>
        이름:
        <input type="text" value={name} onChange={(e) => setName(e.target.value)} />
      </label>
      <br />

      <label>
        성별:
        <select value={gender} onChange={(e) => setGender(e.target.value)}>
          <option value="남자">남자</option>
          <option value="여자">여자</option>
        </select>
      </label>
      <br />

      <label>
        이메일:
        <input type="email" value={email} onChange={handleEmailChange} />
        {!emailValid && (
          <p style={{ color: "red", marginTop: 0 }}>
            유효한 이메일 주소를 입력해주세요.
          </p>
        )}
      </label>
      <br />

      <label>
        비밀번호:
        <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      </label>
      <br />

      <label>
        생년월일:
        <input type="date" value={birthdate} onChange={(e) => setBirthdate(e.target.value)} />
      </label>
      <br />

      <label>
        <input type="checkbox" checked={agree} onChange={(e) => setAgree(e.target.checked)} />
        약관에 동의합니다.
      </label>
      <br />

      <button type="submit">가입</button>
    </form>
  );
}

 

이메일 유효성 검사 정규 표현식

const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

 

@ 앞뒤로 문자 존재 여부

도메인에 . 포함 여부 검사

공백 또는 잘못된 기호는 허용하지 않음

 

 

코드 흐름 요약

  1. 사용자가 입력 폼에 정보를 입력
  2. useState 훅을 통해 각 항목의 값이 상태로 저장됨
  3. 이메일 입력 시 handleEmailChange 함수로 유효성 실시간 검사
  4. 가입 버튼 클릭 시 handleSubmit 함수 실행
    • 이름/이메일/약관 동의 항목에 대해 유효성 검사
    • 문제 없으면 alert()으로 정보 출력

'React' 카테고리의 다른 글

훅(Hook)  (0) 2025.04.15
컴포넌트와 Props  (0) 2025.04.02
리액트JSX  (0) 2025.03.25
소프트웨어 설계 3주차  (1) 2025.03.18
소프트웨어 설계 2주차  (1) 2025.03.18

Hooks

:함수 컴포넌트에서 React state와 생명주기 기능을 연동할 수 있게 해주는 함수

 

-리액트의 state와 생명주기 기능에 갈고리를 걸어 원하는 시점에 정해진 함수를 실행되도록 만드는 것

=>이때 실행되는 함수가 훅

-훅의 이름은 모두 use로 시작

 

useState( ) 기반 state 사용하기

• 함수 컴포넌트에서는 기본적으로 state를 제공하지 않기 때문에 state를 사용하려면 useState() 훅을 사용해야 함

• React 라이브러리를 로드할 때 useState도 함께 로드

 

import React, { useState } from "react";

 

사용법

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

useState()를 호출할 때는 state의 초기값을 파라미터로 전달

• 리턴 값 - 배열 1) state로 선언된 변수 2) state의 set 함수

• state 로 선언된 변수는 반드시 set 함수만을 사용해서 변경해야 함

• state 변수가 변경되면 이를 참조하는 UI는 자동으로 재렌더링 작업이 수행됨.

 

 

예시) 카운터 (useState( ) 훅 기반) • 변수 각각에 대해 set함수를 개별 정의하여 사용함.

import React, { useState } from "react";
function Counter(porps) {
const [count, setCount] = useState(0);
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={ () => {
setCount(count+1);
 console.log(count);
}}>클릭</button>
</div>
);
}
export default Counter

**setCount는 바로 반영이 안됨, 컴포넌트가 재렌더링이 되어야 반영됨, 그렇기 때문에 화면 출력결과와 콘솔에서 출력한 count값이 다름 (즉시 반영이 아님, 렌더링된 시점에 state변수가 반영이 됨)

 

 

 

예시) 카운터 (useState( ) 훅 기반) – 함수 업데이트 방식 • 이전 상태의 값을 사용해야 할 경우 “함수형 업데이트“ 방식으로 처리

import React, { useState } from "react";
function Counter(porps) {
const [count, setCount] = useState(0);
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={ () => {
setCount(prev => prev+1);
setCount(prev => prev+1);
 console.log(count);
}}>클릭</button>
</div>
);
}
export default Counter

재렌더링이 되기 전에 seycount함수 누적해서 수정이 됨

여러번 했을떄 횟수만큼 반영이 됨

기존의 최신값을 이루어지도록 사용?

 

 

useEffect() - Side effect를 수행하기 위한 훅

 

- 다른 컴포넌트에 영향을 미칠 수 있으며 렌더링 중에는 작업이 완료될 수 없음

- 즉, 렌더링이 끝난 이후에 실행되어야 하는 작업

 

클래스 컴포넌트에서 제공하는 생명주기 함수 componentDidMount(), componentDidUpdate(), ComponentWillMount()와 동일한 기능을 하나로 통합해서 제공

• useEffect() 훅만으로 위 생명주기 함수와 동일한 기능을 수행할 수 있음

 

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

 

의존성 배열 - – 이 이펙트가 의존하고 있는 배열. 배열안에 있는 변수 중 하나라도 값이 변경되면 이펙트 함수가 실행됨

이펙트 함수는 처음 컴포넌트가 렌더링 된 이후, 업데이트로 재렌더링 이후에 실행됨.

 

의존성 배열이 빈 값 – Effect 함수는 mount, unmount시에 단 한번씩 실행됨

useEffect(이펙트 함수, []);

 

의존성 배열 생략 – 컴포넌트가 업데이트 될 때마다 호출

useEffect(이펙트 함수);

 


 

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

 

 

title메시지에 바로 반영됨

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

function Counter(props){
   const [count, setCount] = useState(0);

   useEffect(() => {
    document.title = `${count}번 클릭`;//title메시지에 바로 반영됨
   }, [count]);

    return(
        <div>
            <p>{count}</p>
            <button onClick={()=>{
                setCount((prev) => prev +1);
               
            }}> 클릭
                
            </button>
          
        </div>
    );
}
export default Counter

 

 

title메시지에 바로 반영 안됨

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

function Counter(props){
   const [count, setCount] = useState(0);

   useEffect(() => {
   // document.title = `${count}번 클릭`;
   }, [count]);

    return(
        <div>
            <p>{count}</p>
            <button onClick={()=>{
                setCount((prev) => prev +1);
                document.title = `${count}번 클릭`;//title메시지에 바로 반영 안됨
            }}> 클릭
                
            </button>
          
        </div>
    );
}
export default Counter

 

빈 배열이면 title에 반영 안됨

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

function Counter(props){
   const [count, setCount] = useState(0);

   useEffect(() => {
    document.title = `${count}번 클릭`;
   //}, [count]);
}, []); //이렇게 빈 배열이면 title에 반영안됨
    return(
        <div>
            <p>{count}</p>
            <button onClick={()=>{
                setCount((prev) => prev +1);
               
            }}> 클릭
                
            </button>
          
        </div>
    );
}
export default Counter

 

 

배열 생략 컴포넌트 업데이트 될때마다 title도 동일하게 변경됨

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

function Counter(props){
   const [count, setCount] = useState(0);

   useEffect(() => {
    document.title = `${count}번 클릭`;
    });//배열 생략 컴포넌트 업데이트 될떄마다 title도 동일하게 변경됨

    return(
        <div>
            <p>{count}</p>
            <button onClick={()=>{
                setCount((prev) => prev +1);
               
            }}> 클릭
                
            </button>
          
        </div>
    );
}
export default Counter

실습. useState()와 useEffect()를 이용한 자동 증감 카운터

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

function Counter(props) {
    // count: 현재 카운트 숫자 (초기값 0)
    // setCount: count를 변경하는 함수
    const [count, setCount] = useState(0);

    // isRunning: 카운터 작동 여부를 저장하는 상태 (초기값 false = 정지 상태)
    // setIsRunning: isRunning을 변경하는 함수
    const [isRunning, setIsRunning] = useState(false);

    // useEffect: 컴포넌트가 렌더링되거나 의존값이 바뀔 때 실행됨
    useEffect(() => {
        if (!isRunning) return; 
        // isRunning이 false면 아무 것도 하지 않음 → 카운터 멈춤

        // 1초(1000ms) 뒤에 count를 1 증가시킴
        const timer = setTimeout(() => {
            setCount(count + 1); 
            // 주의: count는 이전 상태값이므로, 배열에 count를 넣어줘야 최신 값 반영 가능
        }, 1000);

        // 클린업 함수: setTimeout이 쌓이지 않도록 정리
        return () => clearTimeout(timer);
    }, [count, isRunning]); 
    // 의존성 배열: count 또는 isRunning이 바뀔 때마다 useEffect 실행됨

    return (
        <div>
            <p>{count}</p> {/* 현재 count 값 화면에 표시 */}
            <button onClick={() => {
                setIsRunning(!isRunning);
                // 버튼 클릭 시 isRunning 값을 반전시킴 (START ↔ STOP)
            }}>
                {isRunning ? 'STOP' : 'START'}
                {/* 현재 상태에 따라 버튼 텍스트 변경 */}
            </button>
        </div>
    );
}

export default Counter;

 

 

useMemo란?

"비싼 계산 결과를 기억(Memoization)해서 불필요한 연산을 줄여주는 훅"

 
// 오래 걸리는 계산을 수행하는 함수
function compute(x, y) {
  // 이 부분은 계산량이 많은 작업이라고 가정
  // 예: 복잡한 수학 계산, 반복문, 대용량 데이터 처리 등
}

// React 컴포넌트
function MyComponent({ x, y }) {
  // 컴포넌트가 렌더링될 때마다 compute(x, y)가 실행됨
  // 즉, x나 y가 바뀌지 않아도 매번 동일한 계산을 다시 함
  // 계산량이 많은 작업일 경우 렌더링 속도 저하 발생 가능
  const value = compute(x, y);

  // 계산된 값을 화면에 출력함
  return <div>{value}</div>;
}

 동작 방식

  • x 또는 y가 바뀔 때만 computeExpensiveValue() 함수를 실행함.
  • 그 외에는 **이전에 계산해 둔 값(memoized value)**을 재사용함.
  • 즉, 불필요한 계산을 줄여서 성능 향상에 도움!

Memoization(메모이제이션)이란?

같은 입력 → 같은 출력이면,
결과값을 저장해서 재사용하는 기법

 왜 필요할까?

  • 반복해서 동일한 계산을 할 필요 없음
  • CPU 리소스 아낌
  • UI 렌더링 속도 향상

 예시

 
function computeExpensiveValue(a, b) { console.log(" 계산 중..."); // 시간이 오래 걸리는 작업이라고 가정 return a + b; }

이 함수가 매번 호출된다면 → 불필요한 낭비!
👉 useMemo()로 감싸면, 값이 변하지 않는 한 딱 한 번만 계산됨!

상황useMemo 필요 여부

단순한 값 계산 ❌ 필요 없음
반복적으로 실행되는 무거운 계산 ✅ 사용하는 것이 좋음
렌더링 성능 저하가 체감되는 경우 ✅ 추천

 

사용법

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

• 의존성 배열의 요소 값이 업데이트될 때만 콜백 함수를 다시 호출

- momoization된 값을 업데이트 후 다시 memoization 해줌

• useMemo()로 전달된 함수는 렌더링이 일어나는 동안 실행됨

• 렌더링이 일어나는 동안 실행되서는 안되는 작업은 useMemo()에 사용하면 안됨

- useEffect()에서 실행돼야 할 이펙트 작업

 

 

 

실습)) usememo() 느리게 작동하는 숫자 제곱 함수

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

function BigSquare() {
  // 📌 number: 입력 숫자 (제곱 계산의 대상)
  // 📌 setNumber: number 값을 변경하는 함수
  const [number, setNumber] = useState(0);

  // 📌 count: 단순히 +1씩 증가하는 카운터 값
  // 📌 setCount: count 값을 변경하는 함수
  const [count, setCount] = useState(0);

  // 🐢 느리게 계산되는 함수 (일부러 느리게 만들었음)
  const slowSquare = (num) => {
    console.log('계산 중...'); // 매번 계산될 때마다 콘솔 출력
    let result = 0;

    // 이 반복문은 실제로 제곱 계산이 느리게 되도록 만든 예제용
    for (let i = 0; i < 2500000000; i++) {
      result = num * num;
    }

    return result;
  };

  // 🚀 useMemo: number 값이 바뀔 때만 slowSquare(number) 호출
  // 그렇지 않으면 이전에 계산된 제곱 값을 재사용함 (성능 최적화!)
  const squared = useMemo(() => slowSquare(number), [number]);

  return (
    <div>
      <h2>느린 제곱 계산기</h2>

      {/* 사용자 입력 필드: 입력한 숫자가 number에 저장됨 */}
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(Number(e.target.value))}
      />

      {/* 계산된 제곱 결과 출력 */}
      <p>제곱 결과: {squared}</p>

      {/* count 값 출력 */}
      <p>{count}</p>

      {/* 클릭할 때마다 count 값을 1씩 증가시킴 */}
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

export default BigSquare;

 

*setCount 함수 사용시 useMemo 덕분에 number 값이 바뀌지 않으면 계산 작업이 생략됨. 즉 불필요한 재렌더링 작업이 발생하지 않음.

 

 

useCallback()

: 함수를 메모이제이션 하기 위한 훅

-첫번째 인자로 넘어온 함수를 의존성 배열 내의 값이 변경될 때까지 저장하여 재사용

 

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

  useCallback() 훅을 사용하지 않고 컴포넌트 내에 함수를 정의할 경우, 렌더링이 일어날 때마다 함수가 새로 정의됨

• useCallback() 훅을 사용하면 특정 변수의 값이 변한 경우에만 함수를 다시 정의

 

실습. useCallback( )

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

// FetchComponent: 검색어에 따라 데이터를 가져오는 예제 컴포넌트
export default function FetchComponent() {
  // 📌 keyword: 사용자가 입력한 검색어 상태
  const [keyword, setKeyword] = useState("");

  // 📌 data: 검색 결과를 저장하는 상태
  const [data, setData] = useState("");

  // 📌 fetchData 함수: keyword를 기반으로 가짜 검색 결과를 가져옴
  // useCallback 사용 이유 → 의존성 배열([keyword])에 따라 함수 재생성 방지
  const fetchData = useCallback(() => {
    console.log("데이터 불러오는 중...");

    // 1초 후에 검색 결과를 보여주는 타이머 (API 대체용)
    setTimeout(() => {
      setData(`"${keyword}"에 대한 검색 결과`);
    }, 1000);
  }, [keyword]); // keyword가 바뀔 때만 fetchData 함수가 새로 생성됨

  // 📌 useEffect: keyword가 변경될 때마다 자동으로 fetchData 실행
  useEffect(() => {
    console.log("useEffect()");
    if (keyword) fetchData(); // 검색어가 비어있지 않으면 fetch 실행
  }, [fetchData]); // fetchData가 바뀔 때 실행되므로, keyword 변화 감지 가능

  return (
    <div>
      <h2>검색 예제 (단일 컴포넌트)</h2>

      {/* 사용자 입력: keyword 상태 업데이트 */}
      <input
        type="text"
        placeholder="검색어 입력"
        value={keyword}
        onChange={(e) => setKeyword(e.target.value)}
      />

      {/* 결과 출력 */}
      <p>결과: {data}</p>

      {/* 버튼 클릭 시 수동으로 fetchData 호출 가능 */}
      <button onClick={fetchData}>수동 검색</button>
    </div>
  );
}

 

*차이점

usememo는 데이터임 데이터가 저장되는, usecallback은 함수 함수로 호출

 

 

useRef( )

• useRef() – Reference를 사용하기 위한 훅

• 리액트 레퍼런스 – 특정 컴포넌트에 접근할 수 있는 객체

• useRef()는 레퍼런스 객체를 반환 •

레퍼런스 객체의 .current 속성 – 현재 참조하고 있는 엘리먼트

 

const refContainer = useRef(초기값);

 

• 초기값으로 초기화된 레퍼런스 반환

• 초기값이 null이면 .current 값이 null인 객체 반환 - { current: null }

 

실습. useRef( ) • 예시) input 태그의 포커싱 되도록 수행

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

// 🔹 TextInputWithFocusButton 컴포넌트
// 사용자가 버튼을 누르면 input에 자동으로 포커스를 주고, 값을 채워줌
export default function TextInputWithFocusButton(props) {
  // 📌 useRef 훅: DOM 요소(input)를 직접 참조할 수 있게 해줌
  // inputElem.current로 해당 input DOM에 접근 가능
  const inputElem = useRef(null);

  // 🔹 버튼 클릭 시 실행되는 함수
  const onButtonClick = () => {
    // 📌 focus(): 해당 input 박스로 커서를 자동으로 이동시킴
    inputElem.current.focus();

    // 📌 value 설정: input에 텍스트 값을 직접 넣음
    inputElem.current.value = "test1234";
  };

  return (
    <div>
      {/* 📌 ref 속성을 통해 input 요소를 inputElem에 연결 */}
      <input ref={inputElem} />

      {/* 🔘 버튼 클릭 시 onButtonClick 함수 실행 */}
      <button onClick={onButtonClick}>
        Focus the input
      </button>
    </div>
  );
}

 

* 버튼 클릭시 input 태그가 포커싱 된다.

개념설명

useRef() DOM 요소나 값을 기억할 수 있는 React 훅
ref={inputElem} 해당 input 요소를 inputElem.current로 직접 제어 가능하게 연결
focus() input에 커서(포커스)를 자동으로 이동시키는 메서드
value 직접 설정 input 값을 프로그래밍적으로 변경 가능

 

훅(Hook)의 규칙 정리

 규칙 1. 훅은 무조건 컴포넌트의 최상위 레벨에서만 호출해야 함

  • 반복문, 조건문, 중첩된 함수 내부에서 호출 ❌
  • 컴포넌트가 렌더링될 때마다 항상 같은 순서로 훅이 실행되어야 하기 때문

 규칙 2. 훅은 리액트 함수 컴포넌트 또는 커스텀 훅에서만 호출 가능

  • 일반 자바스크립트 함수에서 훅 사용 ❌
  • 훅은 오직 함수형 컴포넌트 또는 커스텀 훅 내부에서만 사용 가능

 커스텀 훅(Custom Hook) 개념 정리

  • 리액트에서 기본 제공 훅 외에 직접 만든 훅 함수
  • 여러 컴포넌트에서 반복되는 로직을 재사용하기 위해 사용
  • 이름은 반드시 use로 시작
  • 내부에서 useState, useEffect 등 다른 훅 사용 가능
  • 공통 로직을 분리하여 코드 가독성과 유지보수성 향상

커스텀 훅(Custom Hook) 정리

 커스텀 훅이란?

  • 중복된 로직을 함수로 분리한 것
  • 일반 함수처럼 파라미터와 리턴 값을 자유롭게 정할 수 있음
  • 하지만 기본 훅과 동일한 규칙을 따라야 함

 커스텀 훅의 규칙

  1. 이름은 반드시 use로 시작해야 함
  2. 컴포넌트의 최상위에서만 호출해야 함
  3. React 함수형 컴포넌트 또는 다른 훅 안에서만 사용 가능

 이름을 use로 시작하지 않으면?

  • React가 훅인지 인식 못 해서 규칙 위반 검사를 못함
  • 예기치 않은 오류 발생 가능

상태(state) 공유 여부

  • 여러 컴포넌트에서 같은 커스텀 훅을 사용해도
    상태는 각각 따로 관리됨 (독립적임)
  • 즉, 호출된 만큼 각각 개별 상태 생성

'React' 카테고리의 다른 글

리스트(List), 키(Key), 폼(Forms)  (1) 2025.05.11
컴포넌트와 Props  (0) 2025.04.02
리액트JSX  (0) 2025.03.25
소프트웨어 설계 3주차  (1) 2025.03.18
소프트웨어 설계 2주차  (1) 2025.03.18

컴포넌트 기반 구조 (Component-Based)

컴포넌트 : 독립적인 기능을 수행하는 작은 기능 단위 모듈

• 리액트에서는 모든 페이지가 컴포넌트로 구성

• 하나의 컴포넌트는 또 다른 여러개의 컴포넌트 조합으로 구성될 수 있음.

• 레고 블록을 조립하는 것처럼 컴포넌트를 조합해서 사용

 

함수와 리액트 컴포넌트

• 리액트 컴포넌트는 개념적으로 자바스크립트의 함수와 비슷

• 다만 리액트 컴포넌트는 속성(Props)을 입력 받아 이를 이용해 리액트 엘리먼트를 생성 하여 리턴해 준다는 것이 차이점

 

리액트 컴포넌트

리액트 컴포넌트는 객체지향 프로그래밍(OOP)의 클래스와 인스턴스 개념과 유사

  • 클래스 → 붕어빵 틀 (설계도)
  • 인스턴스(객체) → 실제 만들어진 붕어빵

*리액트에서 컴포넌트는 "붕어빵 틀" 같은 역할을 하고, 이를 사용해 만든 **화면 요소들이 "붕어빵"

 

Props

Props(Properties)는 리액트 컴포넌트의 속성을 뜻함
즉, 컴포넌트에 전달할 **데이터(값)**를 의미

컴포넌트에 전달되는 데이터를 담은 자바스크립트 객체
즉, 하나의 컴포넌트에 다른 props(속재료)를 넣어 다양한 결과를 만들 수 있음

 

*붕어빵 비유

  • 붕어빵 틀(컴포넌트)은 같지만,
    속 재료(Props)에 따라 팥붕, 크림붕, 치즈붕처럼 다양한 붕어빵(엘리먼트)이 나올 수 있음

Props의 특징

읽기 전용(Read-Only)

  • Props는 컴포넌트가 받기만 하고, 직접 수정할 수 없음
  • 붕어빵을 찍는 도중에 속재료를 바꿀 수 없는 것과 같음.

Props 값을 변경하려면?

  • 새로운 Props를 전달해야 함
  • 같은 Props가 들어오면 항상 같은 결과(엘리먼트)가 나와야 함.

 

Props 전달 방식 정리

JSX 문법을 사용하면 키-값 형태로 Props를 전달할 수 있고,
내부적으로는 자바스크립트 객체 형태로 변환

또한, Props의 값으로 컴포넌트도 전달 가능
(즉, 컴포넌트 안에 다른 컴포넌트를 넣을 수도 있음.)

 

 

1. JSX 기반 Props 전달 예시

function App() {
  return (
    <Profile
      name="소품" // name이라는 props에 "소품" 전달
      introduction="안녕하세요, 소품입니다." // introduction props 전달
      viewCount={1500} // 숫자 값도 전달 가능 (문자열 X, 중괄호 사용!)
    />
  );
}

위 코드는 내부적으로 아래처럼 변환됨!

const props = {
  name: "소품",
  introduction: "안녕하세요, 소품입니다.",
  viewCount: 1500
};

 

2. Props 값으로 컴포넌트 전달하기

function App() {
  return (
    <Layout
      width={2560} // width 속성 (숫자 값)
      height={1440} // height 속성 (숫자 값)
      header={
        <Header title="소품의 블로그입니다" /> // header props에 Header 컴포넌트 전달
      }
      footer={<Footer />} // footer props에 Footer 컴포넌트 전달
    />
  );
}

컴포넌트 안에 또 다른 컴포넌트를 props로 넘길 수도 있음

 

 


3. JSX 미사용 - React.createElement() 함수 기반

 

React.createElement() 기본 문법

React.createElement(
  type,         // 요소 타입 (예: 'div', 'span', 컴포넌트 등)
  props,        // 속성 객체 (예: { className: "my-class", id: "my-id" })
  ...children   // 자식 요소 (예: 문자열, 다른 React 요소 등)
);
const profileElement = React.createElement(Profile, {
  name: "소품",
  introduction: "안녕하세요, 소품입니다.",
  viewCount: 1500
});

React.createElement()를 사용하면 JSX 없이도 컴포넌트를 만들 수 있음
하지만 코드가 복잡해지기 때문에 JSX를 주로 사용

 

 

실습(댓글 컴포넌트)
Comment.jsx

import React from "react"; // 리액트 라이브러리 불러오기
import imgUserIcon from "../assets/user_icon.png"; // 사용자 아이콘 이미지 불러오기

// 🟢 함수 컴포넌트(Function Component)
// Comment 컴포넌트는 name(작성자), comment(댓글 내용)을 props로 받아 화면에 표시함
function Comment(props) {
    // 🟢 스타일 객체 정의
    const styles = {
        wrapper: { // 전체 컨테이너 스타일 (댓글 박스)
            margin: 8,
            padding: 8,
            display: "flex", // 가로 정렬
            flexDirection: "row", // 요소들을 가로로 배치
            border: "1px solid grey", // 회색 테두리
            borderRadius: 16, // 테두리를 둥글게
        },
        imageContainer: {}, // 프로필 이미지 컨테이너 (추후 스타일 추가 가능)
        image: { // 프로필 이미지 스타일
            width: 50, // 가로 크기
            height: 50, // 세로 크기
            borderRadius: 25, // 원형 모양
        },
        contentContainer: { // 텍스트 컨테이너 스타일
            marginLeft: 10, // 왼쪽 여백 추가
            display: "flex",
            flexDirection: "column", // 세로 정렬
            justifyContent: "center", // 중앙 정렬
        },
        nameText: { // 이름 스타일
            color: "black",
            fontSize: 16,
            fontWeight: "bold",
        },
        commentText: { // 댓글 내용 스타일
            color: "black",
            fontSize: 16,
        },
    };

    // 🟢 컴포넌트 렌더링 (Rendering)
     return (
        <div style={styles.wrapper}>
            <div style={styles.imageContainer}>
                <img src={imgUserIcon} alt="user icon" style={styles.image} />
            </div>
            <div style={styles.contentContainer}>
                <span style={styles.nameText}>{props.name}</span>
                <span style={styles.commentText}>{props.comment}</span>
            </div>
        </div>
    );
}

export default Comment;

 

CommentList.jsx(여러개의 댓글 보여주는 리스트)

import React from "react"; // 리액트 라이브러리 불러오기
import Comment from "./Comment"; // Comment 컴포넌트 불러오기

// 🟢 함수 컴포넌트(Function Component)
// 여러 개의 Comment 컴포넌트를 조합(컴포넌트 합성)하여 CommentList를 생성
function CommentList(props) {
    return (
        <div> {/* 전체 댓글 리스트 컨테이너 */}
            {/* 🟢 컴포넌트 합성 (Composition) */}
            {/* 여러 개의 Comment 컴포넌트를 조합하여 리스트를 만듦 */}
            <Comment name="홍길동" comment="안녕하세요. 댓글 남깁니다~" /> {/* 첫 번째 댓글 */}
            <Comment name="유재석" comment="리액트 재미있어요~!" /> {/* 두 번째 댓글 */}
            <Comment name="강민경" comment="저도 리액트 배워보고 싶어요!!" /> {/* 세 번째 댓글 */}
        </div>
    );
}

// 🟢 CommentList 컴포넌트 내보내기 (다른 파일에서 사용 가능)
export default CommentList;

 

 

index.js

// React 라이브러리를 불러옴 (React 컴포넌트를 만들기 위해 필요)
import React from 'react';

// ReactDOM을 불러옴 (React 요소를 실제 DOM에 렌더링하는 역할)
import ReactDOM from 'react-dom/client';

// CommentList 컴포넌트를 불러옴 (댓글 목록을 표시하는 컴포넌트)
import CommentList from './components/CommentList';

// HTML 문서에서 id가 'root'인 요소를 찾아 React의 렌더링 루트(root)로 지정
const root = ReactDOM.createRoot(document.getElementById('root'));

// React 컴포넌트를 화면에 렌더링하는 함수
root.render(
  <React.StrictMode> {/* StrictMode는 잠재적인 문제를 감지하고 경고를 표시하는 개발 모드 */}
    <CommentList /> {/* CommentList 컴포넌트를 화면에 렌더링 */}
  </React.StrictMode>
);

*root.render()란?

root.render()는 React 컴포넌트를 실제 화면에 그리는 역할

 

 

실습2(컴포넌트기반스터디룸현황UI 제작)

• 총5개의스터디룸현황표시

• 이미지, 이름, 상태 텍스트 출력

 

 

Room.jsx(방 정보 컴포넌트)

// React 라이브러리를 불러옴 (React 컴포넌트를 만들기 위해 필요)
import React from "react";

// 방 아이콘 이미지 파일을 불러옴
import imgUserIcon from "../assets/room_icon.png";

// Room(방) 정보를 나타내는 컴포넌트
function Room(props) {
    // 스타일을 정의하는 객체
    const styles = {
        wrapper: {
            margin: 20, // 외부 여백
            padding: 8, // 내부 여백
            width: 300, // 가로 크기
            height: 400, // 세로 크기
            display: "flex", // flexbox 사용
            flexDirection: "column", // 세로 방향 정렬
            alignItems: "center", // 가로 중앙 정렬
            justifyContent: "center", // 세로 중앙 정렬
            border: "1px solid grey", // 테두리 추가
            borderRadius: 16, // 모서리를 둥글게
        },
        imageContainer: {
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
        },
        image: {
            width: 100, // 이미지 크기 (가로)
            height: 100, // 이미지 크기 (세로)
            borderRadius: 50, // 둥근 이미지 처리
        },
        contentContainer: {
            marginTop: 15,
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
        },
        nameText: {
            marginTop: 30,
            color: "black", // 글자색 검정
            fontSize: 20, // 글자 크기
            fontWeight: "1000", // 글자 두께
            textAlign: "center", // 글자 중앙 정렬
        },
        commentText: {
            marginTop: 30,
            color: "black",
            fontSize: 16,
            fontWeight: "bold",
            textAlign: "center",
        },
    };

    return (
        <div style={styles.wrapper}> {/* 방 정보를 감싸는 컨테이너 */}
            <div style={styles.imageContainer}> {/* 이미지 컨테이너 */}
                <img src={imgUserIcon} alt="room icon" style={styles.image} />
            </div>
            <div style={styles.contentContainer}> {/* 텍스트 정보 컨테이너 */}
                <span style={styles.nameText}>{props.name}</span> {/* 방 이름 */}
                <span style={styles.commentText}>{props.comment}</span> {/* 사용 가능 여부 */}
            </div>
        </div>
    );
}

// Room 컴포넌트를 다른 파일에서 사용 가능하도록 내보내기
export default Room;

 

RoomList.jsx(방 목록을 보여주는 컴포넌트)

// React 라이브러리를 불러옴
import React from "react";

// Room 컴포넌트를 불러옴 (각 방 정보를 표시)
import Room from "./Room";

// RoomList 컴포넌트 정의 (여러 개의 Room을 한 번에 렌더링)
function RoomList(props) {
    return (
        <div> {/* 전체 컨테이너 */}
            <div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap" }}> 
                {/* flexbox를 사용하여 가로 방향으로 나열하고, 줄바꿈 가능하도록 설정 */}
                
                <Room name="[1F] Cube A" comment="사용가능" /> 
                {/* Room 컴포넌트를 사용하여 Cube A 정보를 전달 */}
                
                <Room name="[1F] Cube B" comment="사용가능" /> 
                
                <Room name="[1F] Cube C" comment="사용불가능" /> 
                
                <Room name="[1F] Cube D" comment="사용가능" /> 
                
                <Room name="[1F] Cube E" comment="사용가능" /> 
                
            </div>
        </div>
    );
}

// RoomList 컴포넌트를 다른 파일에서 사용 가능하도록 내보내기
export default RoomList;

 

index.js

// React 라이브러리를 불러옴 (React 컴포넌트를 만들기 위해 필요)
import React from 'react';

// ReactDOM을 불러옴 (React 요소를 실제 DOM에 렌더링하는 역할)
import ReactDOM from 'react-dom/client';

// RoomList 컴포넌트를 불러옴 (방 목록을 표시하는 컴포넌트)
import RoomList from './components/RoomList';

// HTML 문서에서 id가 'root'인 요소를 찾아 React의 렌더링 루트(root)로 지정
const root = ReactDOM.createRoot(document.getElementById('root'));

// React 컴포넌트를 화면에 렌더링하는 함수
root.render(
  <React.StrictMode> {/* StrictMode는 잠재적인 문제를 감지하고 경고를 표시하는 개발 모드 */}
    <RoomList /> {/* RoomList 컴포넌트를 화면에 렌더링 */}
  </React.StrictMode>
);

'React' 카테고리의 다른 글

리스트(List), 키(Key), 폼(Forms)  (1) 2025.05.11
훅(Hook)  (0) 2025.04.15
리액트JSX  (0) 2025.03.25
소프트웨어 설계 3주차  (1) 2025.03.18
소프트웨어 설계 2주차  (1) 2025.03.18

JSX

• A syntax extension to Javascript (자바스크립트 확장 문법)

• Javascript + XML / HTML

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

 

 

역할

• JSX는 내부적으로 XML/HTML 코드를 자바스크립트로 변환함.

• createElement 함수 : JSX 코드를 자바스크립트 코드로 변환하는 역할

// JSX를 사용한 코드
const element = (
<h1 className=""greeting">
Hello, world!
</h1>;
)



// JSX를 사용하지 않은 코드
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
)

 

 

 

• React.createElement()의 결과로 자바스크립트 객체(엘리먼트)가 생성됨

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

 

① type - 엘리먼트의 유형 ,<div> <span>같은 HTML 태그나 다른 리액트 컴포넌트

② props – 엘리먼트의 속성. class, style, src 및 onclick 등의 태그 속성

③ children - 현재 엘리먼트가 포함하는 자식 엘리먼트

 

 

JSX 사용 여부에 따른 코드 비교 

// JSX 사용함
<div>Hello, {name}</div>
// JSX 사용 안함
React.createElement('div', null, `Hello, ${name}`);

 

장점

-코드 간결해짐

-가독성 향상

-보안성 향상: 입력창에 문자나 숫자 같은 소스코드를 입력해, 코드가 실행되도록 만드는 injection Attack해킹 방법을 방어 가능해짐( ex) ID 입력 창에 자바스크립트 코드 )

 

 

ReactDOM은 렌더링하기 전에 임베딩(삽입)된 값을 모두 문자열로 변환

const title = response.pontentiallyMaliciousInput;

//이 코드는 안전
const element = <h1>{title}</h1>;

 

• 위 예시 코드는 JSX 코드에서 중괄호를 사용해서 title 변수를 삽입하며, 이는 보안 위험이 발생할 수 있는 코드의 삽입 가능성이 있음

. • XSS(Cross-site-scripting) 공격을 방어할 수 있음

 

 

 

중괄호를 이용한 변수 참조

const name = "hh";
const element = <h1>Hello, {name}</h1>;
ReactDOM.render (
element,
document.getElementById('root’)
);

 

 

중괄호를 이용한 함수 호출

function formatterName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Hyunwoo',
lastName: 'Nam'
}
const element = (
<h1>
Hello, { formatterName(user) }
</h1>
);
ReactDOM.render (
element, document.getElementById('root')
);

 

태그의 속성에 값을 넣는 방법

 

• 큰따옴표 사이에 문자열을 넣는 방식

const element = <div tabIndex="0"></div>;

 

 

• 중괄호 사이에 자바스크립트 코드를 넣는 방식

const element = <img src={user.avatarUrl}></img>;

 

 

자식(children)을 정의하는 방법

• 평소 HTML 코드와 같이 상위 태그가 하위 태그를 둘러싸도록 작성하면 됨.

const element = (
<div>
<h1>안녕하세요.</h1>
<h2>반갑습니다.</h2>
</div>
);

 

 

 

실습

-저번에 만들어둔 파일))

 

-src안에 components폴더 생성

-components폴더 안에 Book.jsx, Library.jsx파일 생성

 

Book.jsx

import React from "react"; //리액트 라이브러리 가져오기, ui를 구축하는데 사용용

function Book(props){//Book 이라는 함수 + 컴포넌트 정의(props는 부모 컴포넌트에서 전달된데이터 포함)
    return(//jsx를 반환하기 시작, jsx는 js와 html결합한 문법법
        <div>
        <h1>{`이 책의 이름은 ${props.name}입니다.`}</h1>
        <h2>{`이 책은 총 ${props.numOfPage}페이지로 이루어져 있습니다.`}</h2>
        </div>
    );
}
export default Book;//이 컴포넌트를 다른 파일에서 사용할 수 있도록 내보냄냄

 

 

 

Library.jsx

import React from "react";//react 라이브러리 가져옴
import Book from "./Book";//Book컴포넌트를 현재 파일로 가져옴

function Library(props) {
    return(
        <div>
            <Book name="처음 만난 파이썬" numOfPage={300} />
            <Book name="처음 만난 AWS" numOfPage = {400} />
            <Book name ="처음 만난 리액트" numOfPage={500} />

        </div>
    );
}

export default Library;

 

 

index.js수정

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import Library from './components/Library' //Library컴포넌트 불러오기기
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Library /> //이 부분도 불러올 컴포넌트인 Library로 변경경
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

 

컴포넌트 구조

 

 

 

  • 엘리먼트(Element)란?
    • "요소" 또는 "성분"을 의미하며, 리액트 앱을 구성하는 가장 작은 단위.
    • 기존 웹에서 사용하던 DOM 엘리먼트 개념과 유사.
  • 리액트 엘리먼트의 역할
    • 화면에 보이는 UI를 기술하는 자바스크립트 객체.
    • 실제로 웹 브라우저에서 렌더링될 DOM 엘리먼트를 생성하는 기반이 됨.
  • 엘리먼트와 DOM의 관계
    • 리액트 엘리먼트: 화면에 어떤 UI가 나타날지 정의하는 객체.
    • DOM 엘리먼트: 리액트 엘리먼트의 정보를 바탕으로 실제 브라우저에서 렌더링되는 요소.
  • 왜 ‘엘리먼트’라고 부를까?
    • 리액트 초기에는 이를 **Descriptor(설명자)**라고 불렀음.
    • 하지만 결국 DOM 엘리먼트를 나타내므로, 개념적 통일성을 위해 **엘리먼트(Element)**라는 용어를 사용하게 됨.
l const element = <h1>Hello, world!</h1>;

• 리액트의 createElement( ) 함수에 의해 리액트 엘리먼트가 생성됨

 

 

 

엘리먼트의 형태

• 리액트 엘리먼트는 자바스크립트 객체 형태로 존재

• 컴포넌트 유형, 속성, 자식(children)에 대한 정보를 포함하는 자바스크립트 객체

 

 

리액트 엘리먼트

{
	type: 'button',
    props: {
    	className: 'bg-green',
        children: {
        	type:'b',
            props: {
            	children: 'Hello, element!'
                }
            }
        }
}

 

 

DOM엘리먼트

<button class = 'bg-green'>
	<b>
    	Hello, element!
    <b>
 </button>

 

 

 

createElement( ) 함수

• 함수의 실행 결과로 자바스크립트 객체(엘리먼트)가 생성됨

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

① type - 엘리먼트의 유형 ,<div> <span>같은 HTML 태그나 다른 리액트 컴포넌트

② props – 엘리먼트의 속성. class, style, src 및 onclick 등의 태그 속성

③ children - 현재 엘리먼트가 포함하는 자식 엘리먼트

 

 

 

• createElement( ) 함수의 동작 과정

• Buttton 컴포넌트를 포함하는 ConfirmDialog 컴포넌트 구성

function Button(props){
	return(
    	<button className = { `bg-${props.color}`}>
        	<b>
            	{props.children}
            </b>
        </button>
     )
 }
 
 
 function ConfirmDialog(props) {
 	return(
    	<div>
        	<p>내용을 확인하셨으면 확인 버튼을 눌러주세요.</p>
            <Button color = 'green'>확인</button>
        </div>
        )
 }

 

 

createElement( ) 함수의 동작 과정 – ConfirmDialog 컴포넌트

• 현 단계에서 Button 컴포넌트는 아직 HTML 태그가 아니기 때문에 DOM 렌더링이 안됨.

 

{
	type: 'div'
    props:{
    	children: [
        {
        	type: 'p',
            props: {
            	children: '내용을 확인하였으면 확인버튼을 눌러주세요.'
                }
             },
             {
             	type: Button,
                props: {
                	color: 'green',
                    children: '확인'
                    }
                }
           }
       ]
   }
}

 

 

 

• createElement( ) 함수의 동작 과정 – ConfirmDialog 컴포넌트 • 최종적으로 DOM에 렌더링되는 엘리먼트의 구성

{
	type: 'div'
    props:{
    	children: [
        {
        	type: 'p',
            props: {
            	children: '내용을 확인하였으면 확인버튼을 눌러주세요.'
                }
             },
             {
             	type: Button,
                props: {
               		className: 'bg-green',
                    children: {
                    	type: 'b',
                        props: {
                        	children:'확인'
                         }
                    }
                }
           }
       ]
   }
}

 

 

 

• Root DOM node

• root라는 id를 가진 div 태그에 모든 리액트 엘리먼트들이 렌더링되며, 리액트 DOM에 의해 관리됨.

• 리액트만으로 만들어진 앱은 단 하나의 Root DOM Node를 가짐

 

Root DOM node 엘리먼트 렌더링

const element = <h1>안녕, 리액트!<h1>;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);

 

• createRoot로 설정된 Root DOM Node에 render 함수를 사용하여 화면을 렌더링 함.

• 엘리먼트가 렌더링되는 과정은 Virtual DOM에서 실제 DOM으로 이동하는 과정

• 렌더링된 엘리먼트 업데이트

• 엘리먼트는 한번 생성되면 바꿀 수 없기 때문에 다시 생성해야 함

• 즉 기존 엘리먼트를 변경하는 것이 아니라 새로운 엘리먼트를 생성해서 바꿔치기 방식으로 새로운 화면으로 업데이트 할 수 있음.

 

 

 

 

 

실습: 시계 만들기

 

-1초마다 실행: setinterval()함수 사용

-시간 정보 추출

: new Date().toLocaleTimeString() 내장함수 사용

 

 

components폴더 안에 Clock.jsx파일 생성후 작성

import React from "react" //react라이브러리 가져옴

function Clock(props) {//Clock이라는 이름의 함수형 컴포넌트 정의
    return (
        <div>
            <h1>
                현재시간: {new Date().toLocaleDateString() }

            </h1>
        </div>
    )
}

export default Clock;//이 컴포넌트를 다른 파일에서 사용할 수 있도록 내보냄

 

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Clock from './components/Clock'

//DOM의 'root'요소를 선택하여 react가 렌더링 할 루트 생성
const root = ReactDOM.createRoot(document.getElementById('root'));
//1초 간격으로 Clock 컴포넌트 렌더링
setInterval(() => {
  root.render(
    <React.StrictMode>
      <Clock />
      </React.StrictMode>
  );
}, 1000);//1초 후에 컴포넌트 갱신


// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

 

 

 

'React' 카테고리의 다른 글

리스트(List), 키(Key), 폼(Forms)  (1) 2025.05.11
훅(Hook)  (0) 2025.04.15
컴포넌트와 Props  (0) 2025.04.02
소프트웨어 설계 3주차  (1) 2025.03.18
소프트웨어 설계 2주차  (1) 2025.03.18

리액트란

-사용자 인터페이스를 만들기 위한 JavaScript라이브러리

- SPA를 쉽고 빠르게 만들수 있도록 해주는 도구

 

장점

  •  빠른 업데이트와 렌더링 속도
  •  재사용성이 높은 컴포넌트 기반 구조 
  • 메타의 든든한 지원 
  • 활발한 지식 공유와 커뮤니티 
  • 리액트 네이티브를 통한 모바일 앱 개발 가능

 

단점

  • 방대한 학습량
  • 높은 상태 관리 복잡도

 

프레임워크와 라이브러리

-프로그램 흐름에 대한 제어권한차이

-프레임워크는 전체적인 흐름을 갖고 있으며, 애플리케이션의 코드는 프레임워크에 의해 사용됨

-라이브러리는 흐름에 대한 제어를 하지 않고 개발자가 필요한 부분만 가져다 사용  

 

SPA(Single Page Application)

-단일 페이지로 구성된 웹 애플리케이션

*하나의 html 틀을 만들어 놓고 , 사용자가 요청할 그 페이지에 해당하는 콘텐츠를 가져와 동적으로 페이지의 내용을 채워서 보여주는 방식

 

 

 

여러개의 버튼 생성하기

// MyButton 컴포넌트 정의
function MyButton(props) {
    // 클릭 상태를 관리하는 상태 변수와 setter 함수 정의
    const [isClicked, setIsClicked] = React.useState(false);

    // 버튼을 생성하여 반환
    return React.createElement(
        'button', // 'button' HTML 요소 생성
        { onClick: () => setIsClicked(true) }, // 버튼 클릭 시 상태를 true로 변경하는 이벤트 핸들러
        isClicked ? 'Clicked!' : 'Clicked here!' // 클릭 상태에 따라 버튼 텍스트 변경
    );
}

// ButtonGroup 컴포넌트 정의
function ButtonGroup() {
    return React.createElement(
        'div', // 'div' HTML 요소 생성
        null, // div의 속성은 없음
        React.createElement(MyButton), // 첫 번째 MyButton 컴포넌트 생성
        React.createElement(MyButton), // 두 번째 MyButton 컴포넌트 생성
        React.createElement(MyButton)  // 세 번째 MyButton 컴포넌트 생성
    );
}

// DOM의 '#root' 요소를 선택
const domContainer = document.querySelector('#root');

// ButtonGroup 컴포넌트를 렌더링
ReactDOM.render(React.createElement(ButtonGroup), domContainer); // ButtonGroup을 선택한 DOM 요소에 렌더링

 

 

CRA(create-react-app)기반 리액트 프로젝트 생성

 npx create-react-app my-app
cd my-app
npm start

*내장 웹서버를 통해 리액트 프로젝트의 웹 페이지에 접속 가능

'React' 카테고리의 다른 글

리스트(List), 키(Key), 폼(Forms)  (1) 2025.05.11
훅(Hook)  (0) 2025.04.15
컴포넌트와 Props  (0) 2025.04.02
리액트JSX  (0) 2025.03.25
소프트웨어 설계 2주차  (1) 2025.03.18

var를 이용한 변수 선언의 문제점

-# 변수를 덮어 쓸 수 있음

-# 다시 선언(재선언)할 수 있음

 

let을 이용한 변수 선언

-재선언 불가능, 변수 덮어 쓸 수는 있음

let val2 = "let 변수";
 console.log(val2);
 // let은 덮어 쓰기 가능
val2 = "let 변수 덮어 쓰기“;
 console.log(val2);
 // let은 재선언 불가능/ 에러 발생
let val2 = "let 변수 재선언";
 console.log(val2);

 

const를 이용한 변수 선언

-재선언, 덮어쓰기 모두 불가능

const val3 = "const 변수";
 console.log(val3);
 // const 변수는 덮어 쓰기 불가능 / 에러 발생
val3 = "const 변수 덮어 쓰기";
 // const 변수는 재선언 불가능 / 에러 발생
const val3 = "const 변수 재선언";

 

 

 

* 예외적으로 오브젝트 타입(객체, 배열, 함수, 기타)의 데이터는 const로 정의해도 도중에 데이터 수정 가능

* 프리미티브 타입 ( boolean, number, bigint, string, undefined, null,symbol)은 데이터 수정 불가

 

 

객체속성값변경및추가

 // 객체정의
const obj1 = {
  name: "홍길동",
  age: 24,
 };
 console.log(obj1);
 // 속성값변경
obj1.name = "이순신";
 console.log(obj1);
 // 속성값추가
obj1.addr = "Seoul";
 console.log(obj1);

 

 

배열값변경및추가

// 배열정의
const arr1 = ["dog", "cat"];
 console.log(arr1);
 // 첫번쨰값변경
arr1[0] = "bird";
 console.log(arr1);
 // 값추가
arr1.push("monkey");
 console.log(arr1);

*push()함수: 배열 끝에 요소 추가

 

*리액트 개발에서는 const가장 많이 사용

*동적으로 바뀌는 값은 state 으로 관리

*state로 관리하지 않으면서 처리 도중 수정이 필요한 변수만 let으로 선언

 

 

템플릿 문자열

+연산자 사용

const name = "홍길동";
 const age = 24;
 // 기존의 문자열과 변수 결합 방법
const message = "내 이름은 " + name + "입니다. 나이는 " + age + "세 입니다.";
 console.log(message);

 

` 역따옴표 사용

  • `(역 따옴표 또는 백쿼트backquote)로 문자열 감싼다
  • 문자열 내부에 ${}로 감싼 안쪽은 자바스크립트로 입력 가능
  • 변수 외에도 함수 호출 같은 자바스크립트 코드 넣을 수 있음
const name = "홍길동";
 const age = 24;
 // 템플릿 문자열 이용 방법
const message = `내 이름은 ${name}입니다. 나이는 ${age}세 입니다.`;
 console.log(message);

 

 

화살표 함수 () => {}

기존 함수 작성 예

 // 기존함수작성법#1
 function func1(value) {
  return value;
 }
 console.log(func1("func1입니다"));
 // 기존함수작성법#2
 const func2 = function (value) {
  return value;
 }
 console.log(func2("func2입니다"));

 

 

화살표 함수 예) 

const func3 = (value) => {
 return value;
 }
 console.log(func3("func3입니다"));

 

 

*주의점

 

인수가1개인경우에는소괄호( ) 생략이 가능함

const func3 = value => {
 return value;
 }
 console.log(func3("func3입니다"));

 

인수가2개이상인경우소괄호를생략할수없음.

const func3 = value1, value2 => {//에러 발생//(value1, value2)괄호 필요
 return value1 + value2;
 }
 console.log(func3("func3입니다"));

 

 

return생략표기법

•처리를한행으로반환하는경우중괄호와return을생략할수있음.

// 처리를한행으로반환하므로{} 및return문생략
const func4 = (num1, num2) => num1 + num2;
 console.log(func4(10, 20)); // 30

 

• 반환값이여러행일경우에는( )로감싼뒤단일행과같이모아서반환할수있음

const func5 = (val1, val2) => (
 {
 name: val1,  
age: val2,
 }
 )
 console.log(func5("홍길동", 24)); //{name: '홍길동', age: 24}

 

잘못된return 생략표기법.

•중괄호로감싼뒤return을생략할수있지만값이반환되지않는문제가발생함.

// { }로감쌌지만return 구문을이용하지않음.
 const func4 = (num1, num2) => {
  num1 + num2
 };
 console.log(func4(10, 20)); // undefined

 

 

분할대입{ } [ ]

//분할 대입을이용하지않고문자열을출력
const myProfile = {
 name: '홍길동',
 age: 24,
 }
 const message = `내 이름은 ${myProfile.name}입니다. 나이는
${myProfile.age}세 입니다.`;
 console.log(message);
 
 // 분할 대입 이용
const { name, age } = myProfile;
 const message = `내 이름은 ${name}입니다. 나이는 ${age}세 입니다.`;
 console.log(message);

 

•객체분할대입 

//일부만추출하거나순서를바꾸어추출
const myProfile = {
  name: '홍길동',
  age: 24,
 }
 // 일부만추출
const { age } = myProfile;
 // 순서를바꾸어추출
const { age, name } = myProfile;

// 추출한속성에새로운별명을지정하여사용
const myProfile = {
  name: '홍길동',
  age: 24,
 }
 // 콜론(:)기호를이용해다른변수명(별명)을지정.
 const 
{ name: newName, age: newAge } 
= 
myProfile;
 const message = `내이름은${newName}입니다. 
나이는${newAge}세입니다.`;
 console.log(message);

 

 

•배열 분할 대입

•객체와 마찬가지로 배열에서도 분할대입을 이용할 수 있음.

•배열에 분할대입을 할때는 변수선언부에[ ]를 사용하여 배열에 저장된순서에 임의의 변수명을 설정하여 추출할 수 있음.

 

// 배열인덱스를지정해서대입
const myProfile = ["홍길동", 24];
 // 배열인덱스를지정해서대입
const message = `내이름은${myProfile[0]}입니다. 나이는${myProfile[1]}세입니다.`;
 console.log(message);
 
 // 배열에분할대입
 const myProfile = ["홍길동", 24];
 // 배열분할대입
const [ name, age ] = myProfile;
 const message = `내이름은${name}입니다. 나이는${age}세입니다.`;
 console.log(message);

 

디폴트값=

디폴트값은함수의인수나객체를분할대입할경우,

값이존재하지 않을때초기값을설정해주는기능.

 

인수의 디폴트값

//메시지출력함수의인자값을전달하는경우와아닌경우의출력결과
constsayHello = (name) =>console.log(`${name}님, 안녕하세요!`);
 // 인수가정상적으로전달된경우
sayHello('홍길동');
 // 인수가전달되지않은경우//인자값이전달되지않은경우값이존재하지않기때문에undefined로출력
sayHello(); 


// 인수의디폴트값으로출력된경우
constsayHello = (name="게스트") =>console.log(`${name}님, 안녕하세요!`);
 // 인수가전달되지않은경우//인수값이전달되지않은경우디폴트값“게스트”를출력함
sayHello();

 

 

•객체분할 대입의 디폴트값

•인수와 마찬가지로 변수명뒤에=로값을 설정하면 속성이 존재하지 않은 경우에 설정할 초기값을 지정할 수 있음.

//존재하지 않는 속성을 출력
 const myProfile = {  
  age: 24,
 }
 const { name } = myProfile;
 const message = `${name}님, 안녕하세요!`;
 console.log(message); 
 
 
 
 //분할대입시의디폴트값을설정
 const myProfile = {  
  age: 24,
 }
 const { name=“게스트” } = myProfile;
 const message = `${name}님, 안녕하세요!`;
 console.log(message);

 

스프레드구문...

. . . 과 같이 점 세 개를 연결해 배열에 내부요소를순차적으로전개할수 있는표기법

 

• 1) 요소 전개

 • 스프레드구문

const arr1 = [1, 2];
 console.log(arr1); //[1,2]
 console.log(...arr1);//1 2

 

• 일반적인함수와스프레드구문

const arr1 = [1, 2];
 const summaryFunc = (num1, num2) => console.log(num1 + num2);
 // 일반적으로 배열값을 전달하는 경우
summaryFunc( arr1[0], arr1[1] ); 
// 스프레드 구문을 이용하는 방법
summaryFunc( ...arr1 );

 

 

• 2) 요소모으기

constarr2 = [1, 2, 3, 4, 5];
 const[num1, num2, ...arr3] = arr2;
 console.log(num1); // 1
 console.log(num2); // 2
 console.log(arr3); // [3, 4, 5]

 

 

• 3) 요소복사와결합

// 두개의배열결합
const arr4 = [10, 20];
 const arr5 = [30, 40];
 // 스프레드구문을이용해복사
const arr6 = [...arr4];
 // 스프레드구문을이용해결합
const arr7 = [...arr4, ...arr5];
 console.log(arr4);  
console.log(arr6);  
console.log(arr7);  

//두개의객체결합
const arr4 = {val1: 10, val2: 20};
 const arr5 = {val3: 30, val4: 40};
 // 스프레드구문을이용해복사
const arr6 = {...arr4};
 // 스프레드구문을이용해결합
const arr7 = {...arr4, ...arr5};
 console.log(arr4); 
console.log(arr6);   
console.log(arr7);

 

*등호(=) 복사하면 복사본 데이터의 변경시 원본 데이터도 변경되는 예상치 못한 동작 발생

 

스프레드를 사용하여 완전히 새로운 데이터를 생성해 복사해야만 한다

const arr8 = [...arr4]; // 스프레드구문을이용해복사

 

 

 

 

map, filter

*배열 처리에서 기존 for문을 대체하여 효율적으로 코드 작성이 가능한 문법

 

 

map함수

기존 for문
const nameArr = ["홍길동", "이순신", "유관순"];
 for(let index=0; index < nameArr.length; index++) {
 console.log(nameArr[index]);
 }
 
 //map함수
 const nameArr = ["홍길동", "이순신", "유관순"];
 nameArr.map((name) => console.log(name));

 

 

map 함수의 인수를이용해 요소 순서대로추출

const nameArr = ["홍길동", "이순신", "유관순"];
 nameArr.map((name, index) => 
console.log(`${index}번째 ${nameArr[index]} 입니다.`));

 

filter함수

*map함수와 동일하며 return 뒤에 조건식을 추가하여 일치하는 것만 반환

const numArr = [1, 2, 3, 4, 5];
 // 홀수(2로나눈나머지가1)인경우만추출
const newNumArr = numArr.filter((num) => {
  return num % 2 === 1;
 });
 console.log(newNumArr); //[1, 3, 5]

 

 

삼항연산자

*if ~ else ~ 구문을 단축시켜줄 수 있는 연산자

조건? 조건이true일때의처리: 조건이false일때의처리

 

//E ?와 :을 이용한예시 
const val1 = 1 > 0 ? "true입니다" : "false입니다.";
 console.log(val1);  
// true입니다.


// 함수 return 부분에 삼항 연산자 이용
const checkSumOver100 = (num1, num2) => {
 return num1+num2 > 100 ? "초과" : "허용 범위"; 
}
 console.log(checkSumOver100(50, 40));
 console.log(checkSumOver100(50, 70));

 

 

 

DOM(Document Object Model)

DOM은HTML 문서의객체기반표현방식으로프로그램에서접근및사용.

•문서객체: HTML 태그를자바스크립트에서사용할수있는객체로만든것

 

 

 

getElementById( ) 메서드를 사용해 문서 객체 1개 선택하기

<html>
 <head>
 <script> 
// 페이지가 완전히 로드된 후 실행될 함수를 지정합니다.
window.onload = function () { 
    // 'header'라는 ID를 가진 HTML 요소를 선택합니다.
    let header = document.getElementById('header'); 
    
    // 선택한 요소의 글자 색상을 주황색으로 변경합니다.
    header.style.color = 'orange'; 
    
    // 선택한 요소의 배경 색상을 빨간색으로 변경합니다.
    header.style.background = 'red'; 
    
    // 선택한 요소의 내용을 'From JavaScript'로 변경합니다.
    header.innerHTML = 'From JavaScript'; 
}; 
</script>
 </head>
 <body> 
    <!-- ID가 'header'인 h1 요소 -->
    <h1 id="header">Header</h1>
 </body>
 </html>

* querySelector( ) 메서드를 사용해 문서 객체 1개 선택하기

* querySelectorAll( ) 메서드를 사용해 문서 객체 여러개 선택하기

 

<script> 
  // 이벤트를연결합니다. 
  window.onload = function () { 
  
    // 변수를선언합니다. 
    let output = ''; 
    for (let i = 0; i < 10; i++) { 
output += '<h1>Header -' + i + '</h1>'; 
} 
    // 문서객체내부의글자를변경합니다. 
document.body.textContent = output; ➊
    // innerHTML 속성을사용합니다. 
// document.body.innerHTML = output; ➋
  }; 
</script>

// +, - 버튼을 클릭할 때마다숫자를증가/감소시켜주는카운터프로그램

<!DOCTYPE html>
<html>
   
 
<body>
    <button id="increase">+</button>

 <span id="count">0</span>
 <button id="decrease">-</button>
 
 </body>

<script>
 let count = 0;
 document.getElementById("increase").addEventListener("click", () => {
 count++;
 document.getElementById("count").innerText = count;
 });
 document.getElementById("decrease").addEventListener("click", () => {
 count--;
 document.getElementById("count").innerText = count;
 });
 </script>
   
     <img id="image">
    </body>
    </html>

'React' 카테고리의 다른 글

리스트(List), 키(Key), 폼(Forms)  (1) 2025.05.11
훅(Hook)  (0) 2025.04.15
컴포넌트와 Props  (0) 2025.04.02
리액트JSX  (0) 2025.03.25
소프트웨어 설계 3주차  (1) 2025.03.18

+ Recent posts