1
2
3
4
5
6
7
8
ㅡㅡㅡ
●
#인텔리제이에서 리엑트 써보기
#StyleExam.jsx
import React from "react";
const styles={ //자바스크립트이 객체로 취급되어 key와 value의 개념을 생각
wrapper:{
marginTop:10,
marginBottom:10,
border:"1px solid red",
borderRadius:16,
width:300
},
nameText:{
// marginTop:10,
// marginBottom:10,
// marginRight:10,
// marginLeft:10
fontSize:40
},
content:{
margin:10,
float:"left"
},
contentSub:{
padding:10
}
}
//1) 2번에 담겨지는 함수
function StyleExam(props){
return(
<div style={styles.wrapper}>
{/* style 직접 스타일을 설정할 때 */}
<h1 className={styles.nameText}>{` 이름 : ${props.name} `}</h1>
<h1>Hello {props.name}!!</h1>
<h1 className={styles.nameText}>즐거운 시간~~~</h1>
</div>
);
}
// ` 이것은 백틱입니다.
// <div style={styles.wrapper}> : 스타일 객체에서 정의된 클래스일 것
// style과 className을 혼용해서 쓸수있다.? 뭐 클래스나 id처럼 호출하는데 다른것도 있지않나요?
//2) 1)번을 담고 있는 함수
function StyleExamList(props){
return(
<div className={styles.content}>
<StyleExam name="홍길동" className={styles.contentSub} />
<StyleExam name="김액트" className={styles.contentSub} />
</div>
);
}
export default StyleExamList;
ㅡㅡㅡ
#index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Fruit from './exam01/Fruit';
import Clock from './exam01/Clock';
import SecondCommentList from './exam01/SecondCommentList';
import StyleExamList from './exam02/StyleExam';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* <SecondCommentList />
<Fruit /> */}
<StyleExamList />
</React.StrictMode>,
document.getElementById('root')
);
/*
setInterval(function(){
root.render(
<React.StrictMode>
<Clock />
<Clock />
<Clock />
</React.StrictMode>,
document.getElementById('root')
);
}, 1000);
*/
// 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();
ㅡㅡㅡ
#결과
●2
#HOOK
Hook : 갈고리
#CountExam.jsx (유즈 스태이트훅)
import React, {useState} from "react"; // ,{useState} : 훅을 쓸려면 이것을 입력
// Hook(훅) : 원래 존재하는 어떤 기능에 마치 갈고리를 거는 것 처럼
// 끼어 들어가 같이 수행되는 것
// 리액트의 state와 생명주기 기능에 갈고리를 걸어
// ★★★원하는 시점에 정해진 함수가 실행★★★되도록 만드는 것
// - state : 리액트 컴포넌트의 변경 가능한 데이터, 각 개발자가 직접 정의해서 사용
// 자바스크립트 객체
//유즈스태이트훅 :
//유즈이펙트훅 :
//컴포넌트는 class로 만들기도하고, function으로 만듭니다.
// 예전에는 class로 만들다가, 이후에는 둘다 섞어서, 지금은 function위주로 사용합니다.
function CountExam(){
// useState() : state 훅
// [변수명, set함수명] = userState(초깃값)
// "count" : 새로운 상태 값을 정의, count라는 변수명을 쓴것입니다.
// count 값이 변경되면 컴포넌트가 ★★★다시 랜더링 ★★★되면서 화면에 새로운 count 값이 표시
const [count, setCount] = useState(0);
return(
<div>
<p>당신은 총 {count} 번 클릭했습니다.</p>
<button onClick={() =>setCount(count +1)} >
{/* count 값이 변경되면 컴포넌트가 재렌더링되면서 화면에
새로운 카운트 값이 표시 */}
Click Me
</button>
</div>
);
//카운트 값이 다시 랜더링이 안되는 기존의 자바스크립트이 방식입니다.(훅과 비교하기위해 기록했습니다.)
// let count =0;
// return(
// <div>
// <p>당신은 총 {count} 번 클릭했습니다.</p>
// <button onClick={ () => count++} >
// {/* count 값이 변경되면 컴포넌트가 재렌더링되면서 화면에
// 새로운 카운트 값이 표시 */}
// {/* <button onClick={test} : 함수호출법 {test} */}
// Click Me
// </button>
// </div>
// );
}
export default CountExam;
ㅡㅡㅡ
#index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Fruit from './exam01/Fruit';
import Clock from './exam01/Clock';
import SecondCommentList from './exam01/SecondCommentList';
import StyleExamList from './exam02/StyleExam';
import CountExam from './exam02/CountExam';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
{/* <SecondCommentList />
<Fruit /> */}
{/* <StyleExamList /> */}
<CountExam />
</React.StrictMode>,
document.getElementById('root')
);
/*
setInterval(function(){
root.render(
<React.StrictMode>
<Clock />
<Clock />
<Clock />
</React.StrictMode>,
document.getElementById('root')
);
}, 1000);
*/
// 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();
#결과
●3
#CounterUseEffect(유즈이펙트훅)
import React, {useState, useEffect} from "react"; //useEffect 유즈이펙트훅 ,번외로 여러 훅 도 있습니다. ex)use에서 검색
// useEffect -> Effect 훅
//#
//side Effect(개발용어) 보통 사이트이펙트라고하면, 개발자의 개발입장에서 의도하지 않는 버그가 일어나는 현상을 사이드 이펙트라고 합니다.
//리엑트에서 side effect는 부정적인 용어가 아닙니다.
// side effect, 단 리엑트에서는 side effect가 부정적인 의미가 아님
// 리엑트에서 효과, 또는 영향을 의미
//useEffect() 훅은 리액트의 함수 컴포넌트에서 사이드 이펙트를 실행할 수 있도록 해주는 훅
//useEffect(이펙트 함수, 의존성 배열) : 배열 안에 있는 변수 중에 하나라도 값이 변경되면
// 이팩트 함수가 실행
// 이팩트 함수 mount(실행), unmount(끝낼때)시에 단 한번만 실행되게 하고 싶으면
// 의존성 배열에 빈 배열([])을 넣으면 됨
// 반대로 의존성 배열을 생략하면, 업데이트할때마다, 호출이 됩니다.
function CounterUseEffect(){
const [count, setCount] = useState(0);
// function(){} : 익명함수가 useEffect(()=> 이렇게 사용
// 백틱(`) : 백틱을 사용하면, 계산식에 담게 해줍니다.
// 여기는 의존성 배열을 생략하고 이펙트함수만 넣어놨습니다. 업데이트할떄마다, 랜더링이 된다는것(호출된다는 것)
useEffect(()=>{
//브라우저 API를 이용해 문서의 타이틀을 업데이트 함
document.title = `당신은 총 ${count}번 클릭했습니다.`;
});
return(
<div>
<p>당신은 총 {count} 번 클릭했습니다.</p>
<button onClick={()=> setCount(count+1)} >
Click Me
</button>
</div>
);
}
//이펙트함수, 의존성배열, 넣으면
export default CounterUseEffect;
#결과
#훅의규칙
/*
Hook의 규칙
1. 무조건 "최상위 레벨"에서 "호출"해야합니다. ()
반복문이나 조건문 또는 중첩된 함수들 안에서 훅을 호출하면 안된다는 의미
컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 함
그래야 순서대로 랜더링이 진핸된다는 의미
#최상위 레벨이란?
: 이 컴포넌트는 일반적으로 애플리케이션의 기본 구조를 정의하고,
다른 컴포넌트들을 렌더링하거나 조직하는 역할을 합니다.
따라서 리액트에서 "최상위 레벨"이란 보통 이러한 루트 컴포넌트를 의미합니다.
#
네, 일반적으로 리액트 애플리케이션에서 최상위 컴포넌트는 보통 index.js 파일에서 렌더링됩니다.
#참고
이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장됩니다.
이러한 점은 React가 useState 와 useEffect 가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해줍니다.
2. "리엑트 컴포넌트에서만" "훅을 호출"해야 함
훅은 리액트 함수 컴포넌트에서 호출하거나 직접 만든 커스텀 훅에서만 호출할 수 있음
*/
#이벤트핸들러
●4ConfirmButton.jsx
#
import React, {useState} from "react";
function ConfirmButton(props){
// 변수명 set함수명 = useState(초깃값)
const[isConfirmed, setIsConfirmed] = useState(false);
//#)화살표함수 (익명함수), ( 화살표 함수,와 람다 함수(자바에서불림))
//클릭 이벤트 처리하기
const handleConfirm = () =>{ // () =>{ 과 function(){ 이것은 동일합니다.
// prevIsConfirmed : 해당 함수의 바로 직전의 state가 전달됨
setIsConfirmed( (prevIsConfirmed) => !prevIsConfirmed); //화살표함수 : 펑션 2개 안쓸려고 썻습니다.
// setIsConfirmed(!isConfirmed)
//이전값을 완전히 보존하고 싶어서 리턴하기 위해 이렇게 처리
}
//#) 기본
// //클릭 이벤트 처리하기
// const handleConfirm = function(){
// // prevIsConfirmed : 해당 함수의 바로 직전의 state가 전달됨
// setIsConfirmed( (prevIsConfirmed) => !prevIsConfirmed); //화살표함수 : 펑션 2개 안쓸려고 썻습니다.
// // setIsConfirmed(!isConfirmed)
// //이전값을 완전히 보존하고 싶어서 리턴하기 위해 이렇게 처리
// }
return(
<button onClick={handleConfirm} disabled={isConfirmed}>
{isConfirmed ? "확인됨" : "확인하기"}
</button>
);
}
export default ConfirmButton;
#index.js
#NameForm.jsx
import React, {useState, useEffect} from "react";
function NameForm(props){
// state 훅 [변수명, set함수명]
const [value, setValue] = useState(""); // useState("") : 빈 문자열 넣어줫습니다.
//event : 이벤트 객체
//event.target : 현재 발생한 이벤트의 타켓 (input element)
const handleChange = (event) => {
//set함수를 사용해서 새롭게 변경된 값을 value라는 이름의 state에 저장
//현재 발생한 이벤트의 타켓 value 속성값((input element의 값)
//셋벨류, 이벤트가 발생한 곳에 타겟 , 타겟 .value
setValue(event.target.value);
};
const handleSubmit = (event) =>{
//alert('입력한 이름 : ' + value); //홑따옴표 처리
alert(`입력한 이름 : ${value}`);//백틱 처리 //1. jstl과 비슷한 개념이라 보면되나요? 2. $은 jquery인가요? 그냥 사용방법인가요?
event.preventDefault(); // 이벤트 취소
//alert : 콘솔창에서 확인
//document.write : 본문에서 확인
};
//
return(
<form onSubmit={handleSubmit}>
{/* value={value} : 리액트 컴포넌트의 state에서 값을 가져다 넣어 주는 것
그래서 항상 "state"에 들어 있는 값이 input에 표시 됨
onChange={handleChange} : 함수명을 호출하는 것
*/}
이름 : <input type="text" value={value} onChange={handleChange} />
<button type="submit">전송</button>
</form>
);
}
export default NameForm;
//어떤 것을 하려고 하시는지
#index.js
#결과
# event.preventDefault();
//event.preventDefault()은 이벤트의 기본 동작을 취소하는 메서드입니다
//관련 사이트
https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault
#앞으로 해야할 것(스프링부트와 연동)
#리엑트 라우터
#리엑트 테스터
●5
#SignUp.jsx
import React, {useState} from "react";
function SignUp(props){
const [name, setName] = useState("");
const [gender, setGender] = useState("여자");
const handleChangeName = (event) => {
setName(event.target.value);
};
const handleChangeGenger =(event) => {
setGender(event.target.value);
}
const handleSubmit = (event) => {
alert(`이름 : ${name}, 성별 : ${gender}`);
event.preventDefault();
};
return(
<form onSubmit={handleSubmit}>
이름 : <input type="text" value={name} onChange={handleChangeName} />
<br />
성별 :
<select value={gender} onChange={handleChangeGenger} >
<option value="남자">남자</option>
<option value="여자">여자</option>
</select>
<button type="submit">전송</button>
</form>
);
}
export default SignUp;
#index.js
#결과
#
새터미널열어서 경로설정 c:\react > 설정
1.clear : 콘솔 지움
2.cd.. :(cd점점) 이전의 경로로 들어갑니다.
3.npx create-react-app miniproject(miniproject 프로젝트명) : 프로젝트 생성
#프로젝트명에 대문자 사용하면 에러납니다.
#정상 결과(소문자로만 구성 나머지는 - 로 연결)
#최상위로 올리기(저렇게 폴더를 설정하면 됩니다.)
●6
글 목록 보기 기능(리스트 형태) : PostList, PostListItem
글 보기 기능 : Post
댓글 보기 기능 : CommentList, CommentListItem
글 작성 기능 : PostWrite
댓글 작성 기능 : CommentWrite
—-----------------------------------------------------------
component
list : 리스트와 관련된 컴포넌트들을 모아 놓을 폴더
CommentList.jsx
CommentListItem.jsx
PostList.jsx
PostListItem.jsx
page : 페이지 콤포넌트들을 모아놓은 폴더
MainPage.jsx
PostViewPage.jsx
PostWritePage.jsx
ui : ui 컴포넌트들을 모아 놓은 폴더
Button.jsx : 글작성/댓글 작성 완료 후 버튼을 눌렀을 때 내용을 저장
TextInput.jsx : 사용자로부터 문자열을 입력 받는데 사용
글이나 댓글을 작성하기 위해 사용
#Topㅡdown(설계할때 좋습니다. 큰그림 그리고 작은 부분을 구체화시킵니다.)
위에서부터 내려오는 방식
#buttomㅡUp(구현할때, (개발할때)에 좋습니다. 작은것부터 구현)
작은것부터 구현해서 올라가는 방식
#네이밍
모두가 알아 볼 수 있는 이름
설계,디자인,기획자 함께 작업
# npm install --save react-router-dom styled-components 입력
ㅡ참고
이 명령어는 npm(Node Package Manager)을 사용하여 두 가지 패키지를 설치하는 것입니다. 설치할 패키지는 다음과 같습니다:
- react-router-dom: 리액트 애플리케이션에서 라우팅을 관리하기 위한 라이브러리입니다. 이를 사용하면 다른 URL에 따라 다른 컴포넌트를 렌더링할 수 있습니다. 예를 들어, 사용자가 /home, /about, /contact 등의 경로를 방문할 때 각각 다른 컴포넌트를 표시할 수 있습니다.
- styled-components: 리액트 애플리케이션에서 CSS 스타일을 구성하는 데 사용되는 라이브러리입니다. 이를 사용하면 JavaScript 코드 내에서 CSS 스타일을 정의하고 컴포넌트에 스타일을 적용할 수 있습니다. 이를 통해 컴포넌트 기반의 스타일링을 구현할 수 있으며, CSS 클래스 이름 충돌과 같은 문제를 방지할 수 있습니다.
--save 옵션은 패키지를 프로젝트의 package.json 파일에 추가하고, dependencies 항목에 설치된 패키지의 버전 정보를 저장합니다. 이를 통해 프로젝트를 다른 환경에서 실행할 때 필요한 패키지를 쉽게 설치할 수 있습니다.
#스타일 사이트
(https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting)
●7
#배열은 맵으로 처리했습니다.
[]대괄호 : 배열
#data.json
[
{
"id": 1,
"title" : "react 테스트1",
"content" : "hello~~\n",
"comments": [
{
"id" : 11,
"content" : "json 데이터 댓글 연습11"
},
{
"id" : 12,
"content" : "json 데이터 댓글 연습12"
},
{
"id" : 13,
"content" : "json 데이터 댓글 연습13"
},
{
"id" : 14,
"content" : "json 데이터 댓글 연습14"
},
{
"id" : 15,
"content" : "json 데이터 댓글 연습15"
}
]
},
{
"id": 2,
"title" : "react 테스트2",
"content" : "hello~~\n",
"comments": [
{
"id" : 21,
"content" : "json 데이터 댓글 연습21"
},
{
"id" : 22,
"content" : "json 데이터 댓글 연습22"
},
{
"id" : 23,
"content" : "json 데이터 댓글 연습23"
},
{
"id" : 24,
"content" : "json 데이터 댓글 연습24"
},
{
"id" : 25,
"content" : "json 데이터 댓글 연습25"
}
]
}
]
#CommentList.jsx
import React from 'react';
import styled from 'styled-components';
import CommentListItem from './CommentListItem';
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
:not(:last-child) {
margin-bottom: 16px;
}
`;
function CommentList(props) {
const { comments } = props; // const { comments } : 배열로 들어오고 있습니다.
return (
<Wrapper>
{ /* 배열.map() : 각 댓글 객체를 CommentListItem */}
{comments.map((comment, index) => {
// 맵 키와 밸류으로 처리합니다.
return (
<CommentListItem key={comment.id} comment={comment} />
);
})}
</Wrapper>
);
}
export default CommentList;
#CommentListItem.jsx
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 8px 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgrey;
}
`;
const ContentText = styled.p`
font-size: 16px;
white-space: pre-wrap;
`;
function CommentListItem(props) {
//props에서 comment 객체 하나만 사용
// comment : 사용자가 작성한 댓글 내용이 들어 있음
const { comment } = props;
return (
<Wrapper>
{/* styled-components를 통해 만든 ContentText 컴포넌트를 이용해서
화면에 표시
*/}
<ContentText>{comment.content}</ContentText>
</Wrapper>
);
}
export default CommentListItem;
#PostList.jsx
import React from 'react';
import styled from 'styled-components';
import PostListItem from './PostListItem';
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
:not(:last-child) {
margin-bottom: 16px;
}
`;
function PostList(props) {
const { posts, onClickItem } = props;
return (
<Wrapper>
{posts.map((post, index) => {
return (
<PostListItem
key={post.id}
post={post}
onClick={() => {
onClickItem(post);
}}
/>
);
})}
</Wrapper>
);
}
export default PostList;
#PostListItem.jsx
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgrey;
}
`;
const TitleText = styled.p`
font-size: 20px;
font-weight: 500;
`;
function PostListItem(props) {
const { post, onClick } = props;
return (
<Wrapper onClick={onClick}>
<TitleText>{post.title}</TitleText>
</Wrapper>
);
}
export default PostListItem;
#MainPage.jsx
import React from 'react';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import PostList from '../list/PostList';
import Button from '../ui/Button';
import data from '../../data.json';
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
:not(:last-child) {
margin-bottom: 16px;
}
`;
function MainPage(props){
// react-router-dom의 useNavigate(); : 훅입니다.
// 페이징 이동을 위해 사용
const navigate = useNavigate();
return(
<Wrapper>
{/* 대문자 : 첫글자가 대문자로나오면 사용자정의태그(xml태그)
소문자 : */}
<Container>
{/* 글을 작성할 수 있는 버튼 */}
<Button title="글 작성하기" onClick={() => {
navigate('/post-write');
}} />
{/* 글 목록을 보여주는 부분 */}
<PostList
posts={data} onClick={(item)=>{
navigate('/post/${item.id}');
}}
/>
</Container>
</Wrapper>
);
}
export default MainPage;
#Button.jsx
import React from "react";
import styled from "styled-components";
const StyledButton = styled.button`
padding: 8px 16px;
font-size: 16px;
border-width: 1px;
border-radius: 8px;
cursor: pointer;
`;
function Button(props) {
// props로 받은 title이 버튼에 표시되도록
// props로 받은 onClick은 StyledButtond의 onClick에 넣어 줌으로써
// 클릭 이벤트를 상위 컴포넌트에서 받을 수 있도록 설정
const { title, onClick } = props;
return <StyledButton onClick={onClick}>{title || "button"}</StyledButton>;
}
export default Button;
#TextInput.jsx
import React from "react";
import styled from "styled-components";
const StyledTextarea = styled.textarea`
width: calc(100% - 32px);
${(props) =>
props.height &&
`
height: ${props.height}px;
`}
padding: 16px;
font-size: 16px;
line-height: 20px;
`;
function TextInput(props) {
const { height, value, onChange } = props;
return <StyledTextarea height={height} value={value} onChange={onChange} />;
}
export default TextInput;
●8 자습
'8.React > 1)개념_React' 카테고리의 다른 글
React_개념_Day_05_02 (0) | 2024.04.25 |
---|---|
React_개념_Day_05_01 (1) | 2024.04.25 |
React_개념_Day_04 (0) | 2024.04.24 |
React_개념_Day_03 (1) | 2024.04.18 |
React_설치와개념_Day_01 (0) | 2024.04.16 |