75일차 내용 정리

채공부·2025년 9월 8일

App4(){} : 함수형 component

{ useEffect, useState } : 함수형 component에서 동작하는 Hook

useState(초기값)

  • 배열을 반환
  • [상태값, 상태 변경 함수] 형태로 반환

배열의 구조 분해 할당

배열에 저장된값을 구조 분해 할당해서 상수에 대입

const [state, setState] = useState(초기값)

예시

const arr = [10, "kim"];
const a = arr[0];
const b = arr[1];

   ⭣

  • 구조 분해 할당 문법을 사용하면 이렇게 된다
const [a,b] = arr;

함수형 컴포넌트

  • ReactElement 객체 (jsx 객체)를 return
function AComponent(){
	  
	return <>
		ui 구성      
    </>
}

함수 호출 시점

  1. component 가 활성화되는 시점에 호출
  2. ui 가 update 될 때 또 호출 (상태값이 변경될 때)

함수 안에서 변수를 선언할 일이 별로 없다
재호출시 다시 변수가 초기화되기 떄문이다

⚠️ 함수 내 변수 선언 주의

  • 함수형 컴포넌트는 렌더링될 때마다 함수가 다시 실행된다
    ➜ 함수 안에 선언한 일반 변수는 매번 초기화됨
    ∴ 상태를 유지하려면 useState or useRef 사용

useEffect

  • [ ] 넣으면 최초 한 번만 실행
useEffect(()=>{}, [])

object 의 구조 분해 할당

const obj = {num:1, name:"kim"}
const num = obj.num;
const name = obj.name;

   ⭣

  • object 의 구조분해 할당을 사용하면 이렇게 된다
const {num, name} = obj;

객체 인자 + 구조 분해 할당

인자로 객체를 전달

  • argobject type

  • 객체를 인자로 넘길 수 있다

function test(arg){

}
test({num:1, name:"kim"});

구조 분해 할당으로 받기

함수 인자에서 바로 구조 분해 가능

  • numnumber type , namestring type
  • 함수 인자에서 바로 구조 분해 가능
function test2({num, name}){

}
test2({num:2, name:"park"});

spread 연산자 (...)

배열이나 객체를 펼쳐서 새로운 배열/객체 만들 때 사용
기존 데이터 복사 + 추가 or 변경 가능

const nums = [10,20,30];
const nums2 = [...nums, 40];

const obj = {num:1, name:"kim"};
const obj2 = {...obj, addr:"seoul"};

객체 속성 접근법

object 의 key 값이 변수 안에 들어 있는 경우가 존재

const mem = {num:1, name:"kim"};

점 표기법

키가 고정된 경우 주로 사용

  • mem 의 num 을 참조
const result = mem.num;

대괄호 표기법

  • 키가 변수에 들어있거나 동적일 때 사용
const result2 = mem["num"];
  • 변수에 key가 들어있는 경우
const a = "num";
const result3 = mem[a];

변수로 객체 키 만들기

  • [a]: 1 : 변수 a에 들어있는 값이 키가 된다

  • {a: 1} : 키가 문자 a , 다름에 주의

let a = "num"
let b = "name"
let result = {[a]:1, [b]:"kim"};
// result는 { num:1, name:"kim" }

ReactComponent

  • App5.jsx 생성
function App5() {
    return (
        <div className='container'>
            <h1>ReactComponent 사용하기</h1>
        </div>
    );
}

export default App5;
  • 함수 Fortune 추가
    • 빈 요소(React Fragment)를 사용해야 한다
      ➜ JSX는 하나의 부모 요소만 반환 가능해서 꼭 감싸야 함
function Fortune(){
    const style = {
        color:"red",
        backgroundColor:"#00ff00"
    }
    return<>
        <h2>오늘의 운세</h2>
        <p style={style}>동쪽으로 가면 귀인을 만나요</p>
    </>
}
  • App5 함수 안에 Fortune 컴포넌트 호출
function App5() {
    return (
        <div className='container'>
            <h1>ReactComponent 사용하기</h1>
            <Fortune/>
        </div>
    );
}

➜ 같은 컴포넌트를 여러 번 사용 가능

부모 ➜ 자식 컴포넌트로 데이터 전달

App 컴포넌트 : 최상위 (부모)

  • 자식 컴포넌트에 데이터를 전달할 수 있다

  • Friends 컴포넌트 생성

function Friends(){
    return <>
        <h2>친구 목록</h2>
        <ul>

        </ul>
    </>
}
  • App5 컴포넌트에서 Friends 호출 + 데이터 전달
function App5() {
    // 친구 목록
    const names = ["유재석", "박명수", "정준하"];
    return (
        <div className='container'>
            <h1>ReactComponent 사용하기</h1>
            <Fortune/>
            <Fortune/>
            <Friends names={names}/>
        </div>
    );
}
  • Friends 에서 props 받기
    • args : 부모 component 가 전달한 속성이 object
function Friends(args){
    // args.names 는 출력할 이름이 들어 있는 배열
    return <>
        <h2>친구 목록</h2>
        <ul>
        	{args.names.map(item=><li>{item}</li>)}    
        </ul>
    </>
}

  • Person 컴포넌트 생성
function Person(){
    return<>
    
    </>
}
  • App5에서 Person 호출
function App5() {
    // 친구 목록
    const names = ["유재석", "박명수", "정준하"];
    return (
        <div className='container'>
            <h1>ReactComponent 사용하기</h1>
            <Fortune/>
            <Fortune/>
            <Friends names={names}/>
            <Person person={"노홍철"}/>
        </div>
    );
}
  • 구조 분해 할당으로 props 받기
function Person({person}){
    return<>
        <p>오늘의 인물 : <strong>{person}</strong></p>
    </>
}
  • App5에서 props 2개 전달
<Person person={"노홍철"} color={"blue"} />
  • Person에서 구조 분해로 여러 props 받기
    • 부모 component 가 전달한 속성값을 object의 구조 분해 할당 문법을 이용해서 추출할 수 있다
function Person({person, color}){
    return<>
        <p>오늘의 인물 : <strong style={{backgroundColor:color}}>{person}</strong></p>
    </>
}
  • components 폴더 생성 후 FriendsComponent.jsx 생성
import React from 'react';

// props 에 names 라는 키값으로 배열이 전달되어야 정상 동작하는 component
function FriendsComponent(props) {
    return (
        <>
            <h2>친구 목록</h2>  
            {props.names.map(item=><li>{item}</li>)}
        </>
    );
}

export default FriendsComponent;
  • App6.jsx 생성
    • 외부 component 를 import 해서 사용할 수 있다
import FriendsComponent from './components/FriendsComponent';

function App6() {
    const names = ["유재석", "박명수", "정준하"];
    return (
        <div className="container">
            <FriendsComponent names={names}/>
        </div>
    );
}

React Bootstrap

React Bootstrap

VSCODE 에 React Bootstrap 설치

  1. npm 사이트에서 react bootstrap click

  1. install 코드 복사

  1. VSCODE 에서 Terminal 을 실행해 해당 코드 붙여넣기
npm i react-bootstrap
  • App7.jsx 생성
function App7() {
    return (
        <div className="container">
            <h1>react bootstrap 컴포넌트</h1>
        </div>
    );
}

BootStrap snippet 등록

  1. File ➜ Preferences ➜ Configure Snippets

  1. jsx ➜ javascriptreact

  1. snippet 코드 자동 생성

  2. 복사해서 vs code 설정 json 문서에 작성하기

  3. 복사한 내용을 javascriptreact.json 파일의 { } 안에 넣기

"boostrap 로딩": {
	"prefix": "bootstrap",
	"body": [
		"import 'bootstrap/dist/css/bootstrap.css'"
	],
	"description": "boostrap 로딩"
}

Alerts

  • App7 컴포넌트에 react bootstrap 버튼 추가
// react bootstrap 을 사용하기 위해서는 css 를 먼저 import 하고
import 'bootstrap/dist/css/bootstrap.css'
// 원하는 component 를 react-bootstrap 으로부터 import 해서 사용
import { Button } from 'react-bootstrap';

function App7() {
    const btnColor = "warning";
    return (
        <div className="container">
            <h1>react bootstrap 컴포넌트</h1>
            <Button variant='primary'>버튼 1</Button>
            <Button variant='success'>버튼2</Button>
            <Button variant={"info"}>버튼3</Button>
            <Button variant={btnColor}>버튼4</Button>
        </div>
    );
}
  • App7 컴포넌트에 안에 코드 추가
    • 취소 가능한 Alert 출력하기
<Alert variant="danger" dismissible>
	<Alert.Heading>Oh snap! You got an error!</Alert.Heading>
	<p>
		Change this and that and try again. Duis mollis, est non commodo
		luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
		Cras mattis consectetur purus sit amet fermentum.
	</p>
</Alert>
  • 버튼 추가
<Button variant='danger'>알림 띄우기</Button>
  • 알림을 띄울지 말지 상태값으로 관리하기
const [show, setShow] = useState(false);
<Button variant='danger' onClick={()=>setShow(true)}>알림 띄우기</Button>
{ show &&
	<Alert variant="danger" dismissible>
		<Alert.Heading>알림</Alert.Heading>
		<p>삭제 실패</p>
	</Alert>
}

Modals

  • components 폴더 안에 MyModal.jsx 생성
    • Modal.Dialog : 모달의 기본 구조
    • Modal.Header : 제목 및 닫기 버튼
    • Modal.Body : 모달 내용
    • Modal.Footer : 하단 버튼 영역
    • 스타일에 display : block 을 줘서 모달을 항상 띄우는 형태
import React from 'react';
import { Button, Modal } from 'react-bootstrap';

function MyModal(props) {
  return (
    <div
      className="modal show"
      style={{ display: 'block', position: 'initial' }}
    >
      <Modal.Dialog>
        <Modal.Header closeButton>
          <Modal.Title>Modal title</Modal.Title>
        </Modal.Header>

        <Modal.Body>
          <p>Modal body text goes here.</p>
        </Modal.Body>

        <Modal.Footer>
          <Button variant="secondary">Close</Button>
          <Button variant="primary">Save changes</Button>
        </Modal.Footer>
      </Modal.Dialog>
    </div>
  );
}
  • App7 에 MyModal 호출
import MyModal from './components/MyModal';

function App7() {
  return (
    <div className="container">
      <h1>React Bootstrap 컴포넌트</h1>
      <MyModal />
    </div>
  );
}
  • AlertModal.jsx 생성
    • 3 개의 props 를 전달받아서 동작하는 react bootstrap 의 Modal
    • show : boolean type 모달을 띄울지 여부
    • message : Modal 에 띄울 메세지
    • onYes : function type 확인 버튼을 눌렀을때 실행할 함수
import { Button, Modal } from "react-bootstrap";

function AlertModal({show, message, onYes}) {
    return (
        <Modal show={show}>
            <Modal.Header>알림</Modal.Header>
            <Modal.Body>{message}</Modal.Body>
            <Modal.Footer>
                <Button variant="success" onClick={onYes}>확인</Button>
            </Modal.Footer>
        </Modal>
    );
}

export default AlertModal;
  • App7 에서 AlertModal 사용
    • show 값이 true일 때만 알림을 보여준다
    • useState로 상태를 관리한다
const [show, setShow] = useState(true);
<AlertModal show={show} message={"안녕하세요"} onYes={()=>{setShow(false)}}/>

페이징 처리

기본 UI

  • App8.jsx 생성
    • Bootstrap의 pagination 클래스를 사용해 기본 틀 생성
function App8() {

    return (
        <div className="container">
            <h1>페이징 처리 ui</h1>
            <ul className="pagination">
                <li className="page-item disabled">
                    <a className="page-link" href="#">Prev</a>
                </li>
                <li className="page-item">
                    <a className="page-link" href="#">1</a>
                </li>
                <li className="page-item">
                    <a className="page-link active" href="#">2</a>
                </li>
                 <li className="page-item">
                    <a className="page-link" href="#">3</a>
                </li>
                <li className="page-item">
                    <a className="page-link" href="#">Next</a>
                </li>
            </ul>
        </div>
    );
}
  • 페이징에 필요한 정보 준비
const pageInfo = {
	startPageNum: 1,      // 시작 페이지 번호
  	endPageNum: 10,       // 끝 페이지 번호
  	totalPageCount: 13,   // 전체 페이지 수
  	pageNum: 3,           // 현재 페이지
  	list: []              // 글 목록 (여기서는 사용 X)
};
  • 페이지 번호 배열 만들기
    • 페이지 번호를 나열할 때 반복문 대신 range() 함수를 만들어 사용
// 페이징 UI 를 만들때 사용할 배열을 리턴해주는 함수를 만들어 두고 활용하자 
function range(start, end) {
  const result = [];
  for (let i = start; i <= end; i++) {
    result.push(i);
  }
  return result;
}

// 페이지를 출력할 배열을 미리 준비한다
const pageArray = range(pageInfo.startPageNum, pageInfo.endPageNum);

동적으로 페이징 처리

  • 동적 페이지 버튼
    • 페이지 번호를 map( ) 으로 반복 출력
<ul className="pagination">
	{/* Prev 버튼 */}
	<li className={`page-item ${pageInfo.startPageNum === 1 ? 'disabled' : ''}`}>
		<a className="page-link" href="#">Prev</a>
	</li>

	{/* 페이지 번호 버튼 */}
	{pageArray.map(num => (
		<li key={num} className={`page-item ${pageInfo.pageNum === num ? 'active' : ''}`}>
			<a className="page-link" href="#">{num}</a>
		</li>
	))}

	{/* Next 버튼 */}
	<li className={`page-item ${pageInfo.totalPageCount > pageInfo.endPageNum ? '' : 'disabled'}`}>
		<a className="page-link" href="#">Next</a>
	</li>
</ul>
  • 서버 잠시 끄고 React Router 설치
npm install react-router-dom

기존 src 폴더를 Step01Basic 으로 폴더명 변경

  • src 폴더 생성 후 main.jsx 생성
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)
  • index.css 생성

  • src 폴더 안에 router, pages 폴더 생성 후 router 폴더 안에 index.jsx 생성

    • 파일명 index.js or index.jsx 로 작성하면 외부에서
      router 폴더까지만 import 했을 때 자동으로 index.js or index.jsx 파일에서 export default 된 자원을 사용할 수 있다
    • index 는 약속된 파일명
import { createHashRouter } from "react-router-dom";
import App from "../App";

// 페이지에 routing 정보를 배열에 미리 저장해 둔다
const routes = [];

// export 해줄 router 객체를 만든다
const router = createHashRouter([{
    path:"/",
    element:<App/>,
    children: routes.map((route)=>{
        return {
            index: route.path === "/", //자식의 path 가 "/" 면 index 페이지 역활을 하게 하기 
            path: route.path === "/" ? undefined : route.path, // path 에 "/" 두개가 표시되지 않게  
            element: route.element //어떤 컴포넌트를 활성화 할것인지 
        }
    })
}]);
  • main.jsx 에 RouterProvider 추가
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'

// SPA 를 구현하기 위한 RouterProvider 를 import
import { RouterProvider } from 'react-router-dom'
// routing 정보를 담고 있는 router import
import router from './router'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <RouterProvider router={router}/>
  </StrictMode>,
)
  • pages 폴더 안에 Home.jsx 생성
function home() {
    return (
        <div>
            <h1>인덱스 페이지</h1>
        </div>
    );
}

useOutlet

  • App.jsx 에 코드 추가
    • 중첩 라우팅을 위한 useOutlet() 사용
import 'bootstrap/dist/css/bootstrap.css'
import { useOutlet } from 'react-router-dom';

function App() {
    // React Router v6 에서 제공하는 hook
    // 현재 경로에 맞는 자식 route component 를 반환한다
    const currentOutlet = useOutlet();

    return (
        <div className="container">
            {currentOutlet}
        </div>
    );
}
  • index.jsx 에 라우터 생성 및 설정 추가
import Home from "../pages/home";

const routes = [
    // spring boot 서버에 넣어서 실행하면 최초 로딩될때  /index.html 경로로 로딩된다
    // 그럴때도  Home 컴포넌트가 활성화 될수 있도록 라우트 정보를 추가한다
    {path:"/index.html", element: <Home/>},
    {path:"/", element:<Home />},
];
  • Home.jsx 에 네비게이션 링크 추가
import { NavLink } from 'react-router-dom';

function Home() {
    return (
        <div>
            <h1>인덱스 페이지</h1>
            <ul>
                <li>
                    <NavLink to="/study">공부하기</NavLink>
                </li>
                <li>
                    <NavLink to="/play">놀러가기</NavLink>
                </li>
            </ul>
        </div>
    );
}
  • pages 폴더 안에 Study.jsx 생성
function Study() {
    return (
        <>
            <h1>Study 페이지</h1>    
            <p>열심히 공부합시다</p>
        </>
    );
}
  • pages 폴더 안에 Play.jsx 생성
function Play() {
    return (
        <>
            <h1>Play 페이지</h1>    
            <p>즐겁게 놉시다</p>
        </>
    );
}
  • index.jsx 에 routes 안에 라우트 정보 추가
import Home from "../pages/Home";
import Study from "../pages/Study";
import Play from "../pages/Play";

const routes = [
    { path:"/index.html", element: <Home/> },
    { path:"/", element: <Home /> },
    { path:"/study", element: <Study /> },
    { path:"/play", element: <Play /> },
];

export default routes;
  • App.jsx 에 Nav 요소 추가
    • UI 는 react bootstrapNav.Link 이지만
      as={NavLink} 설정으로 동작은 react router 의 NavLink 의 동작을 한다
    • 어디로 이동할 지에 대한 정보는 to="경로" 에 작성하면 된다
<Nav variant='pills'>
	<Nav.Item>
		<Nav.Link as={NavLink} to="/">Home</Nav.Link>
	</Nav.Item>
	<Nav.Item>
		<Nav.Link  as={NavLink} to="/study">Study</Nav.Link>
	</Nav.Item>
	<Nav.Item>
		<Nav.Link  as={NavLink} to="/play">Play</Nav.Link>
	</Nav.Item>
</Nav>

회원 목록

  • pages 폴더 안에 Member.jsx 생성
function Member() {
    return (
        <>
            <h1>회원 목록</h1>
            <table className="table table-striped">>
                <thead>
                    <tr>
                        <th>번호</th>
                        <th>이름</th>
                        <th>주소</th>
                    </tr>
                </thead>
                <tbody>
                    
                </tbody>
            </table>
        </>
    );
}
  • index.jsx 에 routes 안에 라우트 정보 추가
import Member from "../pages/Member";
{path:"/members", element:<Member/>}
  • App.jsx 에 회원 목록 링크 추가
<Nav.Item>
	<Nav.Link  as={NavLink} to="/members">회원 목록</Nav.Link>
</Nav.Item>
  • Member.jsx 에 컴포넌트가 활성화 되는 시점에 회원 목록 받아오기
 // 회원 목록을 상태값으로 관리
const [members, setMembers] = useState([]);

// 컴포넌트가 활성화 될 때 회원 목록 받아오기
useEffect(()=>{
	fetch("/api/v1/members")
	.then(res=>res.json())
	.then(data=>setMembers(data));
}, []);
  • 회원 목록 출력
<tbody>
	{members.map(item=>
		<tr>
			<td>{item.num}</td>
			<td>{item.name}</td>
			<td>{item.addr}</td>
		</tr>
	)}
</tbody>

회원 자세히 보기

  • 목록에 자세히 보기 링크 추가
<h1>회원 목록</h1>
<table className="table table-striped">
	<thead>
    	<tr>
      		<th>번호</th>
     	 	<th>이름</th>
      		<th>자세히</th>
   		</tr>
  	</thead>
 	<tbody>
    	{members.map(item => (
     		<tr key={item.num}>
        		<td>{item.num}</td>
        		<td>{item.name}</td>
        		<td>
          			<NavLink to={`/members/${item.num}`}>보기</NavLink>
        		</td>
      		</tr>
    	))}
  	</tbody>
</table>

➜ 라우터에서 path variable(경로 변수) 을 사용할 수 있다

http://localhost:5173/#/members/1
http://localhost:5173/#/members/2
http://localhost:5173/#/members/3

Path Variable (경로 변수)

  • pages 폴더 안에 MemberDetail.jsx 생성
function MemberDetail() {
    // 자세히 보여줄 회원의 번호를 읽어와서
    
    // API 서버에 해당 회원 한 명의 정보를 받아와서 ui 에 출력한다
    return (
        <>
            
        </>
    );
}
  • index.jsx 에 routes 안에 MemberDetail 라우트 정보 추가
{path:"/members/:num", element:<MemberDetail/>}
  • useParams( ) 를 이용한 Path Variable 사용
function MemberDetail() {
    // 자세히 보여줄 회원의 번호를 읽어와서
    // react router 에서 제공해주는 hook 을 이용해서
    // "/members/:num" 에서 num 에 해당하는 자세히 보여줄 회원의 번호를 얻어낸다
    const {num} = useParams();
    // API 서버에 해당 회원 한 명의 정보를 받아와서 ui 에 출력한다
    const [dto, setDto] = useState({});

    useEffect(()=>{
        fetch(`/api/v1/members/${num}`)
        .then(res=>res.json())
        .then(data=>setDto(data));
    }, []);
    return (
        <>
            <h1>회원 자세히 보기</h1>
            <p>번호 : <strong>{dto.num}</strong></p>
            <p>이름 : <strong>{dto.name}</strong></p>
            <p>주소 : <strong>{dto.addr}</strong></p>
        </>
    );
}

HTML Form 전송 방식 vs React JSON 전송 방식

HTML Form 방식의 전송 방식과 특징

  • <form> 태그를 이용한 기본 전송 방식은
    Content-Type = application/x-www-form-urlencoded
  • <input> 요소에 입력한 내용이 query 문자열 로 변환되어 요청이 body 에 들어간다
  • body 에 포함되는 query 문자열 : name = kimgura&addr=seoul
<form>
	<input type="text" name="name">
	<input type="text" name="addr">
</form>

React에서 JSON 방식으로 데이터 전송

  • React에서는 form 자체를 전송하지 않고, fetch() 또는 axios 같은 JavaScript 로 페이지 전환없이 전송할 내용을 json 문자열로 만들어서 전송

  • 이때 전송 방식은 Content-Type = application/json이며, 요청 body에 JSON 문자열을 포함

{
  "name": "kimgura",
  "addr": "seoul"
}

Spring Boot에서 JSON 요청 처리하기: @RequestBody

  • json 문자열을 전송하면 서버에서 request.getParameter() 로 추출이 되지 않는다
    ➜ 추출 방법이 다르다
  • spring boot 에서는 @RequestBody 어노테이션을 이용하면 쉽게 추출할 수 있다
@PostMapping("/api/members")
public String insert(@RequestBody MemberDto dto) {
    // dto.name, dto.addr 로 접근 가능
    return "ok";
}
profile
학원 공부 내용 정리

0개의 댓글