카테고리 없음

웹등이 공부법 5주차- React.js 입문

heejin0283 2025. 10. 27. 03:31
import useInput from "./../hooks/useInput";
//3가지 hook관련된 팁
//1.함수 컴포넌트, 커스텀 훅 내부에서만 호출가능
//2.조건부로 호출될수는 없다
//3.커스텀 훅을 직접만들수있다

const HookExam = () => {
    const [input1, onChange1] = useInput();
    const [input2, onChange2] = useInput();

    return (
        <div>
            <input value={input1} onChange={onChange1} />
            <input value={input2} onChange={onChange2} />
        </div>
    );
};

export default HookExam;
import './App.css';
import {useState} from "react";
function App() {
  const [count,setCount]=useState(0);
  const [light,setLight]=useState("OFF");
  return (
    <>
      <div>
        <h1>{light}</h1>
        <button onClick={()=>{
          setLight(light === 'ON'?"OFF":"ON");
        }}>{light==="ON"?"끄기":"켜기"}</button>
      </div>
      <div>
      <h1>{count}</h1>
      <button onClick={()=>{
        setCount(count+1);
      }}>+</button>
      </div>
      </>
  );
}

export default App;

5.1)실습 준비하기

ESLint라는 도구는 우리가 작성하는 코드를 정적으로 검사해서 혹시나 오류가 발생할만한 코드가 있으면 경고를 띄워주는 프로그램입니다. 코드를 실행해보기 전에 미리 오류 vscode상에서 확인할 수가 있기 때문에 되도록이면 사용하는 것이 좋습니다.

 

 

5.2)React 컴포넌트

function App() {

  return (
    <>
    <h1>안녕 리액트</h1>
    </>
  )
}

이렇게 HTML태그들을 반환하는 함수를 특별히 component라고 부릅니다.

그리고 이 컴포넌트를 부를때에는 보통은 이함수의 이름을 따서 부릅니다.

따라서 지금 이 컴포넌트는 App 컴포넌트라고 부를 수가 있습니다.

 

function Header(){
  return(
    <header>
      <h1>header</h1>
    </header>
  )
}

이렇게 JavaScriot함수를 평범하게 만들고 해당함수가 그냥 HTML 태그들을 리턴하도록 만들어 주게 되면 이제 이함수는 React의 컴포넌트가 됩니다. 그리고 이렇게 함수로 만든 컴포넌트를 React에서는 특별히 함수 컴포넌트라고 부릅니다.

첫번째문자는 꼭 대문자로!!

그림을 보면 App이 가장 위(부모)이고 그 아래에 Header, Main, Footer가 연결되어 있죠.

  • App.jsx → 페이지 전체를 감싸는 최상위 컴포넌트
  • Header.jsx → 상단 메뉴나 제목 부분
  • Main.jsx → 본문 영역 (예: “안녕 리액트!” 문장)
  • Footer.jsx → 하단 저작권 정보
import './App.css'
import Header from "./components/Header"
import Main from "./components/Main"
import Footer from "./components/Footer"
function App() {

  return (
    <>
    <Header />
    <Main />
    <Footer />
    </>
  )
}

export default App
  • import './App.css' → CSS 스타일 파일을 불러와서 이 컴포넌트 전체에 적용합니다.
  • Header, Main, Footer → 각각 다른 파일에 있는 컴포넌트를 가져와 사용합니다.
    • Header.jsx → 상단 영역
    • Main.jsx → 본문
    • Footer.jsx → 하단

-> App.jsx는 “부품들을 조립하는 설계도” 같은 역할이에요.

 

5.3)JSX - UI표현하기

이건 리액트 함수형 컴포넌트의 기본 형태입니다.
이 컴포넌트의 이름은 Footer이고,
return 안에 있는 내용이 실제로 화면에 표시될 UI를 의미합니다.

JSX = JavaScript XML

HTML처럼 생긴 문법을 JavaScript 안에서 사용할 수 있게 만든 확장 문법입니다.

 

동적렌더링의 의미?

동적 렌더링이란
단순히 정해진 문자열을 보여주는 게 아니라,
자바스크립트 코드에서 계산된 결과를 UI에 즉시 반영하는 것을 말합니다.

 

const Main=()=>{
    const number=10;
    
    return(
        <main>
            <h1>main</h1>
            <h1>{number}</h1>
        </main>
    );
};

export default Main;

 

 

중괄호의 의미

JSX(React의 HTML 같은 문법) 안에서는 중괄호 {} 안에 자바스크립트 표현식을 넣을 수 있습니다.

-> 즉, HTML 안에서 JavaScript를 쓸 수 있게 해주는 문법이에요.

//JSX 주의사항
//1.중괄호 내부에는 자바스크립트 표현식만 넣을 수 있다.
//2.숫자,문자열, 배열 값만 렌더링 된다.
//3.모든 태그는 닫혀있어야 한다.
//4.최상위 태그는 반드시 하나여야만 한다.
const Main=()=>{
    const number=10;
    const obj ={a:1};
    return(
        <main>
            <img></img>
            <h1>main</h1>
            <h2>{number %2===0?"짝수":"홀수"}</h2>
            {10}
            {number}
            {[1,2,3]}
            {true}
            {undefined}
            {null}
            {obj.a}
        </main>
    );
};

export default Main;

 

-JSX의 기본규칙

  • 중괄호 {} 안에는 자바스크립트 표현식만 넣을 수 있습니다.
    (if, for 같은 문법은 사용할 수 없습니다.)
  • JSX에서 숫자, 문자열, 배열만 화면에 렌더링됩니다.
    (true, false, null, undefined는 표시되지 않습니다.)
  • 모든 태그는 반드시 닫혀 있어야 합니다.
    (<img>는 <img /> 또는 <img></img>처럼 작성해야 합니다.)
  • JSX는 반드시 하나의 최상위 부모 태그로 감싸야 합니다.
    (<main> 태그처럼 전체를 감싸는 요소가 하나만 있어야 합니다.)
const Main = () => {
  const user = {
    name: "이정환",
    isLogin: true,
  };

  return (
    <>
      {user.isLogin ? (
        <div>로그아웃</div>
      ) : (
        <div>로그인</div>
      )}
    </>
  );
};

 

조건화면에 출력되는 내용

user.isLogin === true <div>로그아웃</div>
user.isLogin === false <div>로그인</div>

 

실행결과:

현재 isLogin 값이 true이므로 화면에는 “로그아웃”이 출력됩니다.

만약 isLogin을 false로 바꾸면 “로그인”이 출력됩니다.

 

const Main=()=>{
    const user ={
        name: "이정환",
        isLogin:true,
    };
    if (user.isLogin){
        return <div>로그아웃</div>
    }
    else{
        return <div>로그인</div>
    }
};

export default Main;

 

user.isLogin이 true → if문 안쪽 코드 실행하므로 return <div>로그아웃</div>이 실행됩니다.

 

//JSX 주의사항
//1.중괄호 내부에는 자바스크립트 표현식만 넣을 수 있다.
//2.숫자,문자열, 배열 값만 렌더링 된다.
//3.모든 태그는 닫혀있어야 한다.
//4.최상위 태그는 반드시 하나여야만 한다.
const Main=()=>{
    const user ={
        name: "이정환",
        isLogin:true,
    };

    if (user.isLogin){
        return (
            <div style={{backgroundColor:"red",
                borderBottom:"5px solid blue"
            }}>
        로그아웃</div>
        );
    }else{
        return <div>로그인</div>
    }
};

export default Main;

 

  • style={} : JSX에서 태그에 스타일을 줄 때 사용하는 문법입니다.
  • 중괄호가 두 겹인 이유?
    • 첫 번째 { } → JSX 안에서 자바스크립트 표현식을 쓰기 위한 중괄호
    • 두 번째 { } → 실제 스타일을 담는 자바스크립트 객체

 

  • CSS 속성 이름은 카멜표기법 으로 써야 합니다.
    (HTML에서는 background-color, React에서는 backgroundColor)
  • 속성 값은 문자열("")로 작성해야 합니다.
    예: "red", "5px solid blue"

 

 

다음과 같이 Main.jsx와 Main.css를 분리할 수도 있습니다.

화면에는 이렇게 구상됩니다.

 

5.4) Props-컴포넌트에 값 전달하기

 

Props?

부모 컴포넌트가 자식 컴포넌트에게 값을 전달하는 방법입니다.

리액트에서 컴포넌트는 함수처럼 입력과 출력을 가질 수 있는데,
그 입력 역할을 하는 것이 바로 props입니다.

 

App.jsx

import './App.css';
import Header from "./components/Header";
import Main from "./components/Main";
import Footer from "./components/Footer";
import Button from "./components/Button";
function App() {
  return (
    <>
      <Button text={"메일"}color={"red"}/>
      <Button text={"카페"}/>
      <Button text={"블로그"}/>

    </>
  );
}

export default App;

 

Button.jsx

const Button=({text,color="black"})=>{
    return (<button style={{color: color}}>
        {text} -{color.toUpperCase()}
        </button>);
};
export default Button;

 

  • props 구조 분해를 이용했습니다
  • ({ text, color = "black" })
    → 부모로부터 전달받은 text와 color를 구조 분해 할당으로 받아옵니다.
    → color="black"은 기본값(default value) 설정입니다.
    즉, color가 전달되지 않으면 자동으로 "black"이 사용됩니다.

결과창은 이렇게 뜨게됩니다.

 

App.jsx

import './App.css';
import Header from "./components/Header";
import Main from "./components/Main";
import Footer from "./components/Footer";
import Button from "./components/Button";
function App() {

  const buttonProps={
    text:"메일",
    color:"red",
    a:1,
    b:2,
    c:3
  }
  return (
    <>
      <Button {...buttonProps}/>
      <Button text={"카페"}/>
      <Button text={"블로그"}>
        <Header />
        </Button>
    </>
  );
}

export default App;

 

Button.jsx

const Button=({children,text,color="black"})=>{
    return (<button style={{color: color}}>
        {text} -{color.toUpperCase()}
        {children}
        </button>);
};
export default Button;

 

자식요소(children) 전달

  • 세 번째 버튼 <Button text="블로그"><Header /></Button>
    → <Header />가 children으로 전달됩니다.
    → Button 안의 {children} 자리에 <Header />가 그대로 렌더링됩니다.

 

화면은 이렇게 출력됩니다.

 

5.5)이벤트처리하기

이벤트 핸들러는

사용자의 행동(이벤트)이 발생했을 때 실행되는 함수입니다.

“버튼 클릭”, “입력창에 글자 입력”, “마우스 올리기” 같은 사용자 행동에 반응하는 코드예요.

const Button=({children,text,color="black"})=>{
    //이벤트객체
    const onClickButton=(e)=>{
        console.log(e);
        console.log(text);
    }
    return (
    <button 
        onClick={(onClickButton)}
        style={{color: color}}
        >
        {text} -{color.toUpperCase()}
        {children}
        </button>);
};
export default Button;

 

  • 이 코드는 버튼 클릭 시 실행되는 이벤트 핸들러를 정의한 예제입니다.
  • console.log(text)는 props로 전달된 버튼의 텍스트를 출력합니다.
  • onClick={onClickButton}으로 버튼과 핸들러 함수를 연결합니다.
  • 즉, 버튼을 클릭하면 이벤트 정보와 text 값이 콘솔에 표시됩니다.

!! 여기서 e는 클릭 시 자동으로 전달되는 이벤트 객체입니다.

console.log(e)는 클릭된 버튼의 이벤트 정보를 출력합니다.

---> 모든 이벤트 객체(e)는 SyntheticEvent라는 리액트 전용 래퍼입니다.

브라우저마다 다른 이벤트 표준을 통일해서 동일한 코드로 작동합니다.

 

6)상태 관리하기

import './App.css';
import {useState} from "react";
function App() {
  const [count,setCount]=useState(0);
  const [light,setLight]=useState("OFF");
  return (
    <>
      <div>
        <h1>{light}</h1>
        <button onClick={()=>{
          setLight(light === 'ON'?"OFF":"ON");
        }}>{light==="ON"?"끄기":"켜기"}</button>
      </div>
      <div>
      <h1>{count}</h1>
      <button onClick={()=>{
        setCount(count+1);
      }}>+</button>
      </div>
      </>
  );
}

export default App;

 

 

 

 

이 코드는 리액트의 상태 관리를 이용한 예제입니다.

count는 숫자를 저장하고, setCount로 값이 바뀝니다.

light는 “ON / OFF” 상태를 저장하고, setLight로 토글됩니다.

첫 번째 버튼은 light가 ON이면 OFF로, OFF면 ON으로 바꿉니다.

두 번째 버튼은 클릭할 때마다 count를 1씩 증가시킵니다.

---<버튼 클릭으로 불빛 ON/OFF 전환숫자 증가가 가능한 코드입니다.

 

5.7)State와 Props

 

import './App.css';
import { useState } from "react";

const Blub = ({ light }) => {
  return (
    <div>
      {light === "ON" ? (
        <h1 style={{ backgroundColor: "orange" }}>ON</h1>
      ) : (
        <h1 style={{ backgroundColor: "gray" }}>OFF</h1>
      )}
    </div>
  );
};

function App() {
  const [count, setCount] = useState(0);
  const [light, setLight] = useState("OFF");

  return (
    <>
      <div>
        <Blub light={light} />
        <h1>{light}</h1>
        <button
          onClick={() => {
            setLight(light === "ON" ? "OFF" : "ON");
          }}
        >
          {light === "ON" ? "끄기" : "켜기"}
        </button>
      </div>

      <div>
        <h1>{count}</h1>
        <button
          onClick={() => {
            setCount(count + 1);
          }}
        >
          +
        </button>
      </div>
    </>
  );
}

export default App;

 

이 코드는 useState 훅을 이용한 상태 관리 예제입니다.

light는 전등 상태(ON/OFF)를 저장하고 setLight로 토글합니다.

Blub 컴포넌트는 light 값에 따라 색상을 주황(ON)/회색(OFF)으로 바꿔 표시합니다.

count는 숫자 상태를 저장하고 setCount로 증가시킵니다.

첫 번째 버튼은 불빛을 켜거나 끄고, 두 번째 버튼은 숫자를 1씩 증가시킵니다.

불빛 토글 + 카운트 증가 기능을 동시에 보여주는 간단한 상태 관리 예제입니다.

 
 

실행화면은 이렇습니다.

 

import './App.css';
import { useState } from "react";

// 🔸 전구 컴포넌트
const Blub = () => {
  const [light, setLight] = useState("OFF");

  return (
    <div>
      {light === "ON" ? (
        <h1 style={{ backgroundColor: "orange" }}>ON</h1>
      ) : (
        <h1 style={{ backgroundColor: "gray" }}>OFF</h1>
      )}
      <h1>{light}</h1>
      <button
        onClick={() => {
          setLight(light === "ON" ? "OFF" : "ON");
        }}
      >
        {light === "ON" ? "끄기" : "켜기"}
      </button>
    </div>
  );
};

// 🔸 카운터 컴포넌트
const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>{count}</h1>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        +
      </button>
    </div>
  );
};

// 🔸 메인 App
function App() {
  return (
    <>
      <Blub />
      <Counter />
    </>
  );
}

export default App;

 

App 컴포넌트는 단순히 <Blub /><Counter /> 두 개의 자식 컴포넌트를 렌더링합니다.

각 컴포넌트(Blub, Counter)는 자신의 상태(state) 를 따로 가지고 있습니다.

Blub → light 상태

Counter → count 상태

따라서 Blub의 상태(light)가 바뀌어도 Counter는 리렌더링되지 않습니다.

반대로 Counter의 상태(count)가 변해도 Blub는 영향을 받지 않습니다.

이렇게 상태를 컴포넌트 내부에 분리하면,
→ 상태가 변경된 해당 컴포넌트만 다시 렌더링되어
불필요한 리렌더링을 방지할 수 있습니다.

 

5.8) State로 사용자입력관리하기

 

register.jsx

import { useState } from 'react'

const Register = () => {
    const [name, setName] = useState("이름");
    const [birth, setBirth] = useState("");
    const [country, setCountry] = useState("");
    const [bio, setBio]=useState("");
    const onChangeName = (e) => {
        setName(e.target.value);
    };

    const onChangeBirth = (e) => {
        setBirth(e.target.value);
    };

    const onChangeCountry = (e) => {
        setCountry(e.target.value);
    };
    const onChangeBio=(e)=>{
        setBio(e.target.value);
    }
    return (
        <div>
            <div>
                <input value={name}
                onChange={onChangeName} placeholder="이름" />
            </div> 
            <div>
                <input value={birth}
                onChange={onChangeBirth} type="date" />
            </div>
            <div>
                <select value={country} onChange={onChangeCountry}>
                    <option value="">선택</option>
                    <option value="kr">한국</option>
                    <option value="us">미국</option>
                    <option value="uk">영국</option>
                </select>
                {country}
            </div>
            <textarea value={bio} onChange={onChangeBio}/>
            {bio}
        </div>
    );
};

export default Register;

 

  • 이 코드는 React의 useState 훅을 사용한 간단한 회원가입 폼(Register 컴포넌트)**입니다.
  • 사용자가 입력한 정보를 실시간으로 상태(state) 로 저장하고, 화면에 즉시 반영합니다.
  • useState 훅을 통해 name, birth, country, bio 네 가지 상태를 관리합니다.
  • 각 입력창의 onChange 이벤트가 실행되면, setName, setBirth, setCountry, setBio 함수가 호출되어 해당 값이 즉시 업데이트됩니다.
  • <input> 태그는 이름과 생년월일 입력을 받고, <select>는 나라 선택을, <textarea>는 자기소개(bio)를 입력받습니다.
  • 사용자가 입력하거나 선택한 값은 컴포넌트 아래에 {country}와 {bio}로 바로 출력되어 양방향 데이터 바인딩이 이루어집니다.
  • 입력 → 상태 업데이트 → 화면 자동 반영이 한 번에 이루어지는 React 상태 관리의 기본 구조를 보여줍니다.

 

실행화면입니다.

 

5.9) State로 사용자입력관리하기

 

import { useState } from 'react'

const Register = () => {
    const [input, setInput] = useState({
        name: "",
        birth: "",
        country: "",
        bio: "",
    });

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

    console.log(input);

    return (
        <div>
            <div>
                <input
                    name="name"
                    value={input.name}
                    onChange={onChange}
                    placeholder="이름"
                />
            </div>
            <div>
                <input
                    name="birth"
                    value={input.birth}
                    onChange={onChange}
                    type="date"
                />
            </div>
            <div>
                <select
                    name="country"
                    value={input.country}
                    onChange={onChange}
                >
                    <option value="">선택</option>
                    <option value="kr">한국</option>
                    <option value="us">미국</option>
                    <option value="uk">영국</option>
                </select>
            </div>
            <textarea
                name="bio"
                value={input.bio}
                onChange={onChange}
            />
            {input.bio}
        </div>
    );
};

export default Register;

 

  • useState로 input 객체 상태(name, birth, country, bio)를 초기화함.
  • 모든 입력창의 변화는 하나의 onChange 함수로 처리함.
  • e.target.name을 통해 어떤 입력창이 바뀌었는지 확인함.
  • [e.target.name]: e.target.value로 해당 필드 값만 업데이트함.
  • 스프레드 연산자(...input)로 기존 값은 유지하고, 바뀐 부분만 수정함.
  • 입력값이 변경될 때마다 input 상태가 실시간으로 업데이트되어 콘솔에 출력됨.
  • 최종적으로 input, select, textarea 모두 같은 로직으로 관리되어 코드가 간결해짐.
5.10) useRef로 컴포넌트의 변수 생성하기
 
 

import { useState, useRef } from 'react'

const Register = () => {
    const [input, setInput] = useState({
        name: "",
        birth: "",
        country: "",
        bio: "",
    });

    const countRef = useRef(0);
    const inputRef = useRef();

    const onChange = (e) => {
        countRef.current++;
        console.log(countRef.current);
        console.log(e.target.name, e.target.value);
        setInput({
            ...input,
            [e.target.name]: e.target.value,
        });
    };

    const onSubmit = () => {
        if (input.name === "") {
            // 이름 입력창으로 포커스 이동
            inputRef.current.focus();
            return;
        }
        alert("제출 완료!");
    };

    return (
        <div>
            <div>
                <input
                    ref={inputRef}
                    name="name"
                    value={input.name}
                    onChange={onChange}
                    placeholder="이름"
                />
            </div>
            <div>
                <input
                    name="birth"
                    value={input.birth}
                    onChange={onChange}
                    type="date"
                />
            </div>
            <div>
                <select
                    name="country"
                    value={input.country}
                    onChange={onChange}
                >
                    <option value="">선택</option>
                    <option value="kr">한국</option>
                    <option value="us">미국</option>
                    <option value="uk">영국</option>
                </select>
            </div>
            <div>
                <textarea
                    name="bio"
                    value={input.bio}
                    onChange={onChange}
                />
            </div>
            <button onClick={onSubmit}>제출</button>
        </div>
    );
};

export default Register;

useState를 사용해 input 상태 객체(name, birth, country, bio)를 관리합니다.
useRef를 통해 countRef는 렌더링 없이 입력 변경 횟수를 추적하고, inputRef는 이름 입력창 DOM을 직접 참조합니다.
사용자가 입력을 변경할 때 onChange 함수가 실행되어 countRef.current 값이 1씩 증가하고, e.target.name과 e.target.value를 이용해 해당 입력 필드 값이 즉시 업데이트됩니다.
제출 버튼을 누르면 onSubmit 함수가 실행되어, 이름이 비어 있을 경우 inputRef.current.focus()로 이름 입력창에 포커스를 이동시키고, 값이 있으면 “제출 완료!”라는 알림이 표시됩니다.
이 코드의 핵심은 useRef는 값 변경 시 렌더링 없이 추적하거나 DOM에 직접 접근할 때 사용되고, useState는 상태를 변경하며 화면을 다시 그리는 역할을 한다는 점입니다.

실행화면은 이렇습니다(focus되었을때)

 

5.11) React Hooks

 

Hooks = 클래스 없이도 state, ref, effect 등 리액트의 핵심 기능을 사용할 수 있게 해주는 기능.

HookExam.jsx

import useInput from "./../hooks/useInput";
//3가지 hook관련된 팁
//1.함수 컴포넌트, 커스텀 훅 내부에서만 호출가능
//2.조건부로 호출될수는 없다
//3.커스텀 훅을 직접만들수있다

const HookExam = () => {
    const [input1, onChange1] = useInput();
    const [input2, onChange2] = useInput();

    return (
        <div>
            <input value={input1} onChange={onChange1} />
            <input value={input2} onChange={onChange2} />
        </div>
    );
};

export default HookExam;

 

useInput.jsx

import { useState } from "react";
function useInput() {
    const [input, setInput] = useState("");

    const onChange = (e) => {
        setInput(e.target.value);
    };

    return [input, onChange];
}

export default useInput;

useInput 파일은 React의 useState를 이용해 입력값 상태를 관리하는 커스텀 훅입니다.
input과 setInput으로 상태를 선언하고, onChange 함수로 입력값이 바뀔 때마다 상태를 갱신합니다.
마지막에 [input, onChange]를 반환하여 외부 컴포넌트에서 사용할 수 있게 합니다.

HookExam 컴포넌트는 useInput 훅을 두 번 호출해 각각의 입력창 상태를 관리합니다.
input1, onChange1, input2, onChange2를 받아 <input>의 value와 onChange에 연결합니다.
사용자가 입력창에 값을 입력하면 onChange가 실행되어 input 상태가 즉시 갱신되고, 그 값이 화면에 반영됩니다.