한 태그로 묶는 것을 Fragment라고 부른다. (화면의 조각)
Exam04.js 생성
첫 번째 방법
import { useState } from "react";
//function Exam04() {}
const Exam04 = ()=> {
const [content, setContent] = useState("");
return (
<>
<div className="container-fluid">
<div className="row mt-2">
<div className="col-md-10 offset-md-1">
<div class="row">
<div class="col">
<h1>네 번째 예제</h1>
<h3>(Q)주말에 뭐하세요?</h3>
</div>
</div>
<div class="row mt-2">
<div class="col">
<textarea name="content" className="form-control" rows="6"
value={content} onChange={e=>setContent(e.target.value)}/>
{/* onChange는 이벤트를 수동으로 발생시켜도 값이 변하지 않는다.
input은 이벤트를 발생시키면 강제로 값이 변한다. 그래서 onChange 선호 */}
</div>
</div>
<div class="row mt-2">
<div class="col text-end">
{content.length} / 1000
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default Exam04;
e.target = event가 발생한 당사자
Exam04.js 생성
두 번째 방법 - 바꾸는 건 하나만 바꾸고 두 개를 쓴다! (내용과 글자수를 다른 State로 잡자)
import { useEffect, useState } from "react";
//function Exam04() {}
const Exam04 = ()=> {
const [content, setContent] = useState("");
const [length, setLength] = useState(0);
//state끼리 의존성이 생기는 경우가 있다.
//- content가 변하면 length가 변해야 한다.
//- 수동으로 하는 것이 아니라 자동으로 변하도록 설정할 수 있다.
//- useEffect 훅 사용
//-useEffect(함수, [감지항목])
useEffect(()=>{
setLength(content.length);
}, [content]);
return (
<>
<div className="container-fluid">
<div className="row mt-2">
<div className="col-md-10 offset-md-1">
<div class="row">
<div class="col">
<h1>네 번째 예제</h1>
<h3>(Q)주말에 뭐하세요?</h3>
</div>
</div>
<div class="row mt-2">
<div class="col">
<textarea name="content" className="form-control" rows="6"
value={content} onChange={e=>setContent(e.target.value)}/>
</div>
</div>
<div class="row mt-2">
<div class="col text-end">
{length} / 1000
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default Exam04;
글자수계산기 완료
Exam05.js 생성
import { useEffect, useState } from "react";
const Exam05 = ()=>{
//state를 3개로 보면 = (java, dbms, boot)
//state를 5개로 보면 = (java, dbms, boot) -> (total, avg)
const [java, setJava] = useState(0);
const [dbms, setDbms] = useState(0);
const [boot, setBoot] = useState(0);;
const [total, setTotal] = useState(0);;
const [avg, setAvg] = useState(0);;
//Effect는 State에만 작성 가능
useEffect(()=>{
setTotal(java + dbms + boot);
}, [java, dbms, boot]);
useEffect(()=>{
setAvg(total/3);
},[total]);
return (
<>
<div className="container">
<div className="row">
<div className="col-md-10 offset-md-1">
<h1>성적 계산기</h1>
</div>
</div>
<div className="row mt-1">
<div className="col-md-10 offset-md-1">
자바 <input type="number" value={java}
onChange={e=>setJava(parseInt(e.target.value))}></input> 점
</div>
</div>
<div className="row mt-1">
<div className="col-md-10 offset-md-1">
데이터베이스 <input type="number" value={dbms}
onChange={e=>setDbms(parseInt(e.target.value))}></input> 점
</div>
</div>
<div className="row mt-1">
<div className="col-md-10 offset-md-1">
스프링부트 <input type="number" value={boot}
onChange={e=>setBoot(parseInt(e.target.value))}></input> 점
</div>
</div>
<hr/>
<div className="row">
<div className="col-md-10 offset-md-1">
총점 = {total}점 , 평균 = {avg}점
</div>
</div>
</div>
</>
);
};
export default Exam05;
Exam06.js 생성
import { useState } from "react";
const Exam06 = () => {
//각각의 상태를 분리하여 관리할 때
// const [alias, setAlias] = useState("");
// const [gender, setGender] = useState("남자");
//상태를 하나의 객체로 관리할 때
const [info, setInfo] = useState({
alias : "",
gender : "남자"
})
// function changeInfo(e){}
const changeInfo = e=>{
// console.log(e.target); //이벤트 발생 태그 확인(e.tartget은 이벤트가 발생한 대상)
// console.log(e.target.name, e.target.value); //이름, 값 확인
//info에서 이벤트가 발생한 태그 명에 해당하는 필드만 입력값으로 바꾸고 나머진 그대로 둬라
//- ...info는 info의 나머지 항목을 의미(rest 연산)
//- 객체에 [] 표시를 쓰면 필드명을 변수로 지정할 수 있다.
setInfo({
...info,
[e.target.name] : e.target.value
});
};
return (
<>
<h1>상태 변수가 객체인 경우</h1>
이름 <input name="alias" type="text" value={info.alias} onChange={changeInfo}/> <br/><br/>
성별
<select name="gender" value={info.gender} onChange={changeInfo}>
<option>남자</option>
<option>여자</option>
</select>
</>
);
};
export default Exam06;
왜 var을 사용하지 않는가?
= var은 오류가 생길 확률이 큼
let a = 10; //가변
const b = 20; //불변
let a = 20;
console.log(a);
배열에 대한 구조할당 연산
...을 스프레드 연산자라고 부른다.
const a = [10, 20, 30];
// const b = a.concat(40);
const b = [...a, 40];
console.log(b);
const a = [10, 20, 30];
const b = [40, 50];
const c = [...a, ...b];
console.log(c);
Exam07.js 생성 객체 상태 변수 문제(회원가입)
import { useState } from "react";
const Exam07 = ()=>{
//객체로 상태 변수를 정의
const [member, setMember] = useState({
memberId:"",
memberPw:"",
memberPwRe:""
});
//객체의 상태를 한 번에 변경하는 함수를 구현
const changeMember = (e)=>{
setMember({
...member,
[e.target.name] : e.target.value
});
};
return (
<div className="container-fluid">
<div className="row">
<div className="col-md-10 offset-md-1">
{/* 점보트론 */}
<div className="p-4 text-light bg-dark rounded">
<h1>객체 상태 변수 문제</h1>
</div>
<form autoComplete="off">
<div className="row mt-4">
<div className="col">
<label className="form-label">아이디</label>
<input type="text" name="memberId" className="form-control"
value={member.memberId} onChange={changeMember}/>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호</label>
<input type="password" name="memberPw" className="form-control"
value={member.memberPw} onChange={changeMember}/>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호 확인</label>
<input type="password" name="memberPwRe" className="form-control"
value={member.memberPwRe} onChange={changeMember}/>
</div>
</div>
</form>
<div className="row mt-4">
<div className="col">
<button type="button" className="btn btn-primary w-100">회원가입</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Exam07;
Exam07.js
import { useEffect, useState } from "react";
const Exam07 = ()=>{
//객체로 상태 변수를 정의
const [member, setMember] = useState({ //입력데이터
memberId:"",
memberPw:"",
memberPwRe:""
});
//입력데이터가 변하면 검사결과가 자동으로 계산되도록 처리
useEffect(()=>{
console.log("member가 변했습니다.")
//id검사
const idRegex = /^[a-z][a-z0-9]{7,19}$/;
const idMatch = idRegex.test(member.memberId);
//pw검사
const pwRegex = /^[A-Za-z0-9!@#$]{8,16}$/;
const pwMatch = pwRegex.test(member.memberPw);
//pw-re검사
const pwReMatch = member.memberPw.length > 0
&& member.memberPw === member.memberPwRe;
setResult({
memberId : idMatch,
memberPw : pwMatch,
memberPwRe : pwReMatch
});
}, [member]); //항목을 안 적으면 처음 한 번만 실행, 항목을 적으면 항목이 변할 때마다 실행
const [result, setResult] = useState ({ //검사결과
memberId:false,
memberPw:false,
memberPwRe:false,
});
//객체의 상태를 한 번에 변경하는 함수를 구현
const changeMember = (e)=>{
setMember({
...member,
[e.target.name] : e.target.value
});
};
return (
<div className="container-fluid">
<div className="row">
<div className="col-md-10 offset-md-1">
{/* 점보트론 */}
<div className="p-4 text-light bg-dark rounded">
<h1>객체 상태 변수 문제</h1>
</div>
<form autoComplete="off">
<div className="row mt-4">
<div className="col">
<label className="form-label">아이디</label>
<input type="text" name="memberId" className="form-control"
value={member.memberId} onChange={changeMember}/>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호</label>
<input type="password" name="memberPw" className="form-control"
value={member.memberPw} onChange={changeMember}/>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호 확인</label>
<input type="password" name="memberPwRe" className="form-control"
value={member.memberPwRe} onChange={changeMember}/>
</div>
</div>
</form>
<div className="row mt-4">
<div className="col">
<button type="button" className="btn btn-primary w-100">회원가입</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Exam07;
Exam07.js
import { useEffect, useState } from "react";
const Exam07 = ()=>{
//객체로 상태 변수를 정의
const [member, setMember] = useState({ //입력데이터
memberId:"",
memberPw:"",
memberPwRe:""
});
//입력데이터가 변하면 검사결과가 자동으로 계산되도록 처리
useEffect(()=>{
console.log("member가 변했습니다.")
//id검사
const idRegex = /^[a-z][a-z0-9]{7,19}$/;
const idMatch = idRegex.test(member.memberId);
//pw검사
const pwRegex = /^[A-Za-z0-9!@#$]{8,16}$/;
const pwMatch = pwRegex.test(member.memberPw);
//pw-re검사
const pwReMatch = member.memberPw.length > 0
&& member.memberPw === member.memberPwRe;
setResult({
memberId : idMatch,
memberPw : pwMatch,
memberPwRe : pwReMatch
});
}, [member]); //항목을 안 적으면 처음 한 번만 실행, 항목을 적으면 항목이 변할 때마다 실행
const [result, setResult] = useState ({ //검사결과
memberId:false,
memberPw:false,
memberPwRe:false,
});
//객체의 상태를 한 번에 변경하는 함수를 구현
const changeMember = (e)=>{
setMember({
...member,
[e.target.name] : e.target.value
});
};
return (
<div className="container-fluid">
<div className="row">
<div className="col-md-10 offset-md-1">
{/* 점보트론 */}
<div className="p-4 text-light bg-dark rounded">
<h1>객체 상태 변수 문제</h1>
</div>
<form autoComplete="off">
<div className="row mt-4">
<div className="col">
<label className="form-label">아이디</label>
<input type="text" name="memberId"
className={`
form-control
${result.memberId ? 'is-valid' : 'is-invalid'}
`}
value={member.memberId} onChange={changeMember}/>
<div className="valid-feedback">멋진 아이디입니다!</div>
<div className="invalid-feedback">사용할 수 없는 아이디입니다!</div>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호</label>
<input type="password" name="memberPw"
value={member.memberPw} onChange={changeMember}
className={`
form-control
${result.memberPw ? 'is-valid' : 'is-invalid'}
`}
/>
<div className="valid-feedback">올바른 형식의 비밀번호입니다.</div>
<div className="invalid-feedback">비밀번호 형식이 올바르지 않습니다.</div>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호 확인</label>
<input type="password" name="memberPwRe"
value={member.memberPwRe} onChange={changeMember}
className={`
form-control
${result.memberPwRe ? 'is-valid' : 'is-invalid'}
`}
/>
<div className="valid-feedback">비밀번호가 일치합니다.</div>
<div className="invalid-feedback">비밀번호가 일치하지 않습니다.</div>
</div>
</div>
</form>
<div className="row mt-4">
<div className="col">
<button type="button" className="btn btn-primary w-100"
disabled={!(result.memberId && result.memberPw
&& result.memberPwRe)}>회원가입</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Exam07;
Exam07.js 문제 해결 후 blur 추가
import { useEffect, useState } from "react";
const Exam07 = ()=>{
//객체로 상태 변수를 정의
const [member, setMember] = useState({//입력데이터
memberId:"",
memberPw:"",
memberPwRe:""
});
const [result, setResult] = useState({//검사결과
memberId:null,
memberPw:null,
memberPwRe:null
});
//입력데이터가 변하면 검사결과가 자동으로 계산되도록 처리
const checkMember = ()=>{
//console.log("member가 변했습니다");
//ID검사
const idRegex = /^[a-z][a-z0-9]{7,19}$/;
const idMatch = member.memberId.length === 0 ? null : idRegex.test(member.memberId);
//PW검사
const pwRegex = /^[A-Za-z0-9!@#$]{8,16}$/;
const pwMatch = member.memberPw.length === 0 ? null : pwRegex.test(member.memberPw);
//PW-RE검사
const pwReMatch = member.memberPwRe.length === 0 ? null :
member.memberPw.length > 0 && member.memberPw === member.memberPwRe;
setResult({
memberId : idMatch,
memberPw : pwMatch,
memberPwRe : pwReMatch
});
};
//useEffect(checkMember, [member]);
//객체의 상태를 한 번에 변경하는 함수를 구현
const changeMember = (e)=>{
setMember({
...member,
[e.target.name] : e.target.value
});
};
return (
<div className="container-fluid">
<div className="row">
<div className="col-md-10 offset-md-1">
{/* 점보트론 */}
<div className="p-4 text-light bg-dark rounded">
<h1>객체 상태 변수 문제</h1>
</div>
<form autoComplete="off">
<div className="row mt-4">
<div className="col">
<label className="form-label">아이디</label>
<input type="text" name="memberId"
className={`
form-control
${result.memberId === true ? 'is-valid' : ''}
${result.memberId === false ? 'is-invalid' : ''}
`}
value={member.memberId} onChange={changeMember}
onBlur={checkMember}/>
<div className="valid-feedback">멋진 아이디입니다!</div>
<div className="invalid-feedback">사용할 수 없는 아이디입니다</div>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호</label>
<input type="password" name="memberPw"
className={`
form-control
${result.memberPw === true ? 'is-valid' : ''}
${result.memberPw === false ? 'is-invalid' : ''}
`}
value={member.memberPw} onChange={changeMember}
onBlur={checkMember}/>
<div className="valid-feedback">올바른 형식의 비밀번호입니다</div>
<div className="invalid-feedback">비밀번호 형식이 올바르지 않습니다</div>
</div>
</div>
<div className="row mt-4">
<div className="col">
<label className="form-label">비밀번호 확인</label>
<input type="password" name="memberPwRe"
className={`
form-control
${result.memberPwRe === true ? 'is-valid' : ''}
${result.memberPwRe === false ? 'is-invalid' : ''}
`}
value={member.memberPwRe} onChange={changeMember}
onBlur={checkMember}/>
<div className="valid-feedback">비밀번호가 일치합니다</div>
<div className="invalid-feedback">비밀번호가 일치하지 않습니다</div>
</div>
</div>
</form>
<div className="row mt-4">
<div className="col">
<button type="button" className="btn btn-primary w-100"
disabled={!(result.memberId === true && result.memberPw === true
&& result.memberPwRe === true)}>
회원가입
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Exam07;
React 배운 내용 요약
React
- SPA(Single Page Application)을 구현할 수 있는 기술이다.
- 라이브러리로 분류하지만 프레임워크의 특징을 가지고 있다.
- NodeJS를 통해 직접 실행/빌드가 가능하도록 구성되어 있다.
- Webpack 시스템을 도입하여 import/export가 가능하게 되어있다.
- create-react-app으로 생성한 프로젝트는 위 환경을 무조건 가지고있다.
- React 앱의 시작지점은 index.js
- React 앱의 빌드와 관련된 파일은 package.json
- 모든 요소들이 index.js를 향하도록 연결해놓고 빌드하면 최종결과물(production)이 나온다.
- 하나로 다 만들면 너무 복잡해지기 때문에 컴포넌트(Component)를 만들어서 화면을 분리하여 개발하도로 구성되어 있다. - 클래스형 컴포넌트 - 함수형 컴포넌트
- 컴포넌트에는 상태(state)가 존재한다.
- useState 훅을 이용하여 생성
- `const [이름, 세터함수] = useState(초기값)`
- 초기값은 단일데이터 or 객체 or 배열 일 수 있다.
- state를 표현식 `{}`를 사용하여 화면의 특정 영역에 출력할 수 있다.
- 컴포넌트에는 이펙트(effect)가 존재한다.
- state 간에 서로 영향을 줄 수 있도록 설정할 수 있다.
- `useEffect(함수, [감지항목])`
- effect는 앱 성능에 중대한 영향을 미치므로 사용을 자제하는게 좋다.
- 만약 감지항목이 없다면 처음에 딱 한 번만 실행한다.