프로젝트 java505_react_test3 생성
필요한 의존성 프로그램들을 넣어주는 파일.
react 용 bootstrap이 따로 존재함
https://react-bootstrap.github.io/
하지만 동작방법이 다르기 때문에 기존 부트스트랩 가져올 거임(버튼 실행하려면 import 사용해야 한다)
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous">
node.js는 반드시 폴더 안에 들어가 그 밑에 패키지를 설치해야 한다.
절대로 이런 상태로 실행하면 안된다
PS C:\java505\intellij\react\java505_react_test3> npm install react-bootstrap bootstrap
생긴 건 똑같다.
<button type={"button"}>기본 버튼</button>
<button type={"button"} className={"btn btn-primary"}>부트스트랩 적용 버튼</button>
<Button type={"button"} variant={"success"}>react용 bootstrap 적용 버튼</Button>
{/*리액트 용 방법은 좀 다르다. react용 button은 대문자로 시작된다. 소문자로 적은건 html태그로 인식한 것이고
대문자로 시작한 건 리액트 컴포넌트로 인식한 것이다.*/}
<></>이것도 인식한다. 부모태그 바깥에 있으면 오류가 남.
import logo from './logo.svg';
import './App.css';
import Button from 'react-bootstrap/Button';
// 기본적으로 7가지를 알아야 한다
// JSX : 자바스크립트 + html / xml , 하나의 파일에 js와 html을 동시에 작성
// 컴포넌트 : 리액트를 구성하는 최소 ui단위, 데이터(props)를 입력받아 view(state) 상태에
// 따라 화면을 출력하는 함수, 컴포넌트 이름은 항상 대문자로 시작한다.(리액트는 소문자로 시작하는 컴포넌트를 html태그로 인식한다)
// ui를 재사용 가능한 개별적인 여러 조각으로 나누어서 화면 구현
// 컴포넌트의 종류 2가지
// 함수형 컴포넌트 : 현재 많이 사용되는 방식, 사용이 간편함, 자바스크립트 함수를 작성하는 방식
// 클래스형 컴포넌트 : 기존에 많이 사용되는 방식, React.Component를 상속받아 구현함, 컴포넌트 구성요소,
// 리액트 생명주기를 모두 포함하고 있음
// props : 컴포넌트 간의 데이터를 주고 받기 위한 객체, properties의 줄임말, 읽기 전용, 부모 컴포넌트에서
// 자식 컴포넌트로 데이터를 전달 시 주로 사용
// state : 현재 컴포넌트의 상태를 표시하는 객체, setState()를 통해서 데이터를 수정, 데이터 수정 시 화면에
// 재랜더링이 됨
// hooks : react 16.8 버전에서 추가된 기능, 상태 변경 및 리액트 생명주기에 관련된 함수를 사용할 수 있게 해주는 기능.
// 리액트 hooks를 사용하면 클래스 컴포넌트를 사용할 필요가 없음
// 컨텍스트 : 데이터 전달 객체, 컴포넌트간의 데이터 전달 시 props를 사용하면 순차적으로 데이터를 전달함,
// 컨텍스트는 위치에 상관없이 데이터를 바로 전달할 수 있음. 원래는 1번 컨테이너에 들어가고 싶다면
// 전체 -> main -> 1이런 순으로 들어가야 하는데, 컨텍스트를 거치면 어느 곳이든 바로 들어갈 수 있다
// react-router : 각 페이지의 경로를 구성하는 라이브러리.
// JSX 문법
// 1. 반드시 1개의 부모 요소가 다른 요소를 감싸는 형태로 구성해야 함
// 리액트는 가상화면을 미리 만들어놓고 다 만드면 실제 화면에 뿌리는 형태이기 때문에 부모 요소가 하나만 존재한다
// 2. 자바스크립트 표현식 사용 가능
// {} 안에 자바스크립트를 사용할 수 있다.
// if문은 표현식이 아니기 때문에 jsx에서 사용할 수 없다 (삼항 연산자를 대신 사용)
// 3. html 속성을 카멜명명법으로 사용해야 한다
// font-size => fontSize로 사용함
// class => className 으로 사용
function App() {
let num1 = 10;
const flag = false;
let result;
//여기서는 if문 사용이 가능하다
// {}표현식 내에서 자바스크립트의 if문을 사용할 수 없으므로 외부에서 미리 연산한다
if (flag){
result = <div>결과가 true</div>
}
else{
result = <div>결과가 false</div>
}
return (
// <></> 이것도 태그로 인식한다.
// <p>안뇽</p>
// 꼭 div태그를 사용해야 하는 건 아님(부모태그로) 그래도 가장 무난한 건 div다.
<div className="App">
<br/><hr/>
<button type={"button"}>기본 버튼</button><br/>
<button type={"button"} className={"btn btn-primary"}>부트스트랩 적용 버튼</button><br/>
<Button type={"button"} variant={"success"}>react용 bootstrap 적용 버튼</Button>
<p>{num1 + 10}</p>
{/*if문을 사용하면 바로 에러가 난다.*/}
{/*{if(flag){*/ }
{/* */}
{/*}}*/}
{/*아래와 같은 삼항연산자를 if문 대신 사용해야 한다*/}
{flag == true ? 1100 : 0}
{/*두 개중 어느 것을 골라도 동일하게 출력이 된다*/}
{result}
<div>{result}</div>
{/*리액트 용 방법은 좀 다르다. react용 button은 대문자로 시작된다.
소문자로 적은건 html태그로 인식한 것이고
대문자로 시작한 건 리액트 컴포넌트로 인식한 것이다.*/}
<br/><hr/>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
대부분 함수 컴포넌트를 많이 사용하는 편이다.
// ClassComponetn.jsx
// js로 해도 작동은 잘 된다
// 1. 모든 컴포넌트는 React를 import해서 사용함
// 2. class 컴포넌트는 React.Component를 상속받아 클래스를 생성
// 3. 클래스 컴포넌트를 export default로 설정해야 내부에서 사용가능하다
// 4. 클래스 컴포넌트에는 render() 메서드가 존재한다. render() 함수에서 jsx문법을 사용함
// 5. 클래스 컴포넌트는 리액트 생명주기 메서드를 사용할 수 있음
// 6. 생성자 사용 가능. 생성자에서 상태표현을 위해 state를 설정
import React from "react";
class ClassComponent extends React.Component {
constructor(props) {
super(props);
this.state={}
}
render() {
return(
<div>
<p>클래스 컴포넌트 화면</p>
</div>
);
}
componentDidMount() {
console.log("마운트 후 출력")
}
}
export default ClassComponent;
// FunctionComponent.jsx
// 1. 모든 컴포넌트는 React를 import해서 사용함
// 2. 함수 컴포넌트는 그냥 함수 생성
// 3. 함수 컴포넌트를 export default로 설정해야 내부에서 사용가능하다
// 4. 함수 컴포넌트는 return에서 jsx문법을 사용한다
// 5. state 변경 및 생명주기에 관련된 함수를 사용하기 위해서 hooks를 사용함
// 대부분 function 컴포넌트에서 해결함. HOOK을 사용하면서 생명주기 사용가능해졌기 때문
import React ,{useState} from "react";
function FunctionComponent(props){
return (
<p>함수컴포넌트 사용</p>
);
}
export default FunctionComponent;
index.js에 을 주석처리하고 를 넣어준다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App2';
import reportWebVitals from './reportWebVitals';
import App2 from "./App2";
import App3 from "./App3";
import App33 from "./App33";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/*<App />*/}
{/* <App2/>*/}
<App3/><-!!!!!!!!!!!! 지금 App3를 사용한다
{/* <App33/>*/}
</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();
그 때 그 때 필요한 폴더를 연결하고 있다
✨StrictMode란 react 앱 내의 잠재적인 문제를 알아내기 위한 도구이다.
//App3.jsx
import React from "react";
import ProFileList from "./folder1/ProfileList";
import BoardList from "./folder1/BoardList";
function App3(){
return(
<div>
<ProFileList/>
<br/>
<BoardList/>
</div>
);
}
export default App3;
이곳에서 여러 파일들을 모아 화면을 만들어 주고 있다.
// ProFileList.jsx
import React from "react";
import Profile from "./Profile";
// props : 컴포넌트 간의 데이터 전달을 위해서 사용하는 자바스크립트 객체. 읽기 전용이다.
// 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달 시 사용한다.
// 부모 컴포넌트에서 자식 컴포넌트를 호출하여 사용 시 속성을 사용하여 데이터를 전달한다.
// 부모 컴포넌트에서 사용한 속성명이 자식 컴포넌트의 props 객체에 담겨서 전달이 된다.
// 해당 속성명을 자식 컴포넌트에서 그대로 사용함
function ProFileList(){
return(
<div className={"row"}>
<div className={"col-sm-6 mx-auto"}>
<Profile userId = {"test1"} userName={"테스터1"} userTel={"01012345678"} userEmail={"test1@naver.com"}/>
<Profile userId ={"test2"} userName={"테스터2"} userTel={"01011111111"} userEmail={"test2@naver.com"}/>
</div>
</div>
);
}
export default ProFileList;
prifile의 데이터를 가져온다
// BoardList.jsx
import React from "react";
import BoardItem from "./BoardItem";
const boardItemList = [
{boardIdx : 100, boardTitle: "게시판 글 제목 100번", boardUserId : "test1", boardCreateDate:"2023-01-03-12:40:00"},
{boardIdx : 101, boardTitle: "게시판 글 제목 101번", boardUserId : "test2", boardCreateDate:"2023-01-03-12:41:00"},
{boardIdx : 102, boardTitle: "게시판 글 제목 102번", boardUserId : "test3", boardCreateDate:"2023-01-03-12:43:00"},
]
function BoardList(){
// boardItemList.map((item )=>{
// return <BoardItem idx ={item.boardIdx} title={item.boardTitle} userId={item.boardUserId} createDt={item.boardCreateDate}/>
// })
return(
<div className={"container mx-5 mx-auto" }>
<table className={"table table-hover table-striped"}>
<thead>
<tr>
<th>글 번호</th>
<th>글 제목</th>
<th>사용자</th>
<th>등록시간</th>
</tr>
</thead>
<tbody>
{/*하나하나 다 넣기 힘드니까 데이터는 리스트로 넘어오니 for문으로 반복해 집어넣어준다*/}
{boardItemList.map((item )=>{
return <BoardItem idx ={item.boardIdx} title={item.boardTitle} userId={item.boardUserId} createDt={item.boardCreateDate}/>
})}
{/*<BoardItem idx={"100"} title={"테스트 제목100"} userId={"test1"} createDt={"2023-01-03 12:40:00"}/>*/}
{/*<BoardItem idx={"101"} title={"테스트 제목101"} userId={"test2"} createDt={"2023-01-03 12:40:00"}/>*/}
{/*<BoardItem idx={"102"} title={"테스트 제목102"} userId={"test3"} createDt={"2023-01-03 12:40:00"}/>*/}
</tbody>
</table>
<BoardItem/>
</div>
);
}
export default BoardList;
///////////////////////////////////
{boardItemList.map((item )=>{
return <BoardItem idx ={item.boardIdx} title={item.boardTitle} userId={item.boardUserId} createDt={item.boardCreateDate}/>
})}
데이터 하나하나 넣어주기 힘드니 반복문을 사용해 집어 넣어준다.
map
파라미터로 전달된 함수를 사용해, 배열 각 요소를 원하는 규칙에 따라 변환한 다음 새로운 배열을 생성한다.
// BoardItem.jsx
import React from "react";
function BoardItem({idx, title, userId, createDt}){
return(
<tr>
<td>{idx}</td>
<td>{title}</td>
<td>{userId}</td>
<td>{createDt}</td>
</tr>
);
}
export default BoardItem;
//Profile.js
import React from "react";
// function Profile({userId, userName,userTel,userEmail}){} 이런 식으로 해도 된다.
// 확장표현식 - 대입연산자 오른쪽의 데이터를 연산자 왼쪽의 변수에 저장시 [] / {} 에 표시된 이름에 대입
// 왼쪽이 오브젝트타입이면 오른쪽이 오브젝트타입이어야 한다. 단! key 값이 동일해야 한다
// 컴포넌트 => 함수다
function Profile(props){
let data = props;
//props는 읽기전용이므로 계산이나 변경하고 싶으면 변수로 받아 해야 한다.
let userId = props.userId;
let userName = props.userName;
let userTel = props.userTel;
let userEmail = props.userEmail;
return (
<div className={"border rounded-2 px-3 m-4"}>
<div className = {"my-3" }>
<label for={"user-id"} className={"form-label"}>아이디 : </label>
<input type={"text"} id={"user-id"} className={"form-control"} value={data.userId}/>
{/*<input type={"text"} id={"user-id"} className={"form-control"} value={props.userId}/>*/}
</div>
<div className = {"my-3" }>
<label for={"user-name"} className = {"form-label" }>이름 : </label>
<input type={"text"} id={"user-name"} className={"form-control"} value={userName}/>
</div>
<div className = {"my-3" }>
<label for={"user-tel"} className = {"form-label" }>전화번호 : </label>
<input type={"tel"} id={"user-tel"} className={"form-control"} value={props.userTel}/>
</div>
<div className = {"my-3" }>
<label for={"user-email"} className = {"form-label" }>이메일 : </label>
<input type={"email"} id={"user-email"} className={"form-control"} value={userEmail}/>
</div>
</div>
)
}
export default Profile;