오늘은 Next.js와 함께 사용할 React에 대해 배워보았다.
오늘 강의에 3단계 구성은 이렇다.
#1. 왜 React인가 => npm trends
#2.컴포넌트란? => react-hooks
#3.컴포넌트 변수 => State
1. 왜 React인가
React를 요약하자면 대표적인 Front-end 도구 라고 말할 수 있다.
React를 포함한 Vue.js, Angular.js 가 대표적인 프론트엔드 언어 중 3가지라고 말할 수 있다.
그래서 왜 많은 사람들이 왜 React를 사용할까?
React에는 3가지 개발 플랫폼으로 나눠서 정의할 수 있는데
- React.js => 페이스북, 인스타, 트위터, 넷플릭스등 웹에서 최적화된 라이브러리
- Reaact-Native => 앱 => 크로스플랫폼 / 안드로이드 (코틀린 / 자바) / ios (Swift,Object-c)
Android & ios에서 동작하는 앱을 만들 수 있다. - React + Eletron => PC 와 앱을 구성하는 PC앱 대표적으로 슬랙이 해당된다.
React의 핵심
React에서 Component란?
컴포넌트는 우리가 개발할 UI 또는 기능을 부품화해 재사용할 수 있는 기능이다.
UI의 재사용
UI를 재사용하면서 안에 있는 컴포넌트만 바꿀 수 있다는 장점
페이지도 하나의 큰 컴포넌트다.
export // 파일을 내보낼때 담당하는 export 이곳을 페이지 컴포넌트 영역이라 부름
// 밑에서부터 함수형 컴포넌트
return (
<div></div>
)
컴포넌트를 만드는 방벙
// 클래스형 컴포넌트
class App extends Component {}
// 함수형 컴포넌트
function New() {}
// const 상수 화살표 함수
const New = () => <div>이것은 화살표 컴포넌트</div>
함수형 컴포넌트의 탄생
Class형에 비해 조금더 코딩량일 줄게 되는 장점이 있다.
하지만 여전히 Class형 컴포넌트를 사용하는 코드도 있다.
React 함수형 컴포넌트 탄생 계기
Hooks는 함수형을 클래스형과 동일한 기능을 사용하도록 만들어 준다.
import {useState, useEffect} from 'react'; // React Hooks라고도 부름
useState 컴포넌트가 변경될때
useEffect 컴포넌트가 최초 그려진후 실행
함수 => 함수형 컴포넌트를 저장하게 만들어줌
function App (props) { // 다른 컴포넌트한테 받은 데이터
const [state] = useState({}) // 컴포넌트 안에보관할 데이터
useState(() => {
// 컴포넌트가 최초 그려진뒤
})
useEffect(() => {
// 컴포넌트가 변경될때 실행
})
}
State
컴포넌트의 변수
기존 방식의 문제점
<head>
<script>
function aaa() {
// 숫자올리는
const temp = Number(document.getElementById("zzz").innerText) + 1;
document.getElementById("zzz").innerText = temp;
}
</script>
</head>
<body>
<div id="zzz">0</div>
<button onClick="aaa()"> 카운트 증가</button>
</body>
JS에서 HTML을 직접 가져오고 넣어주면서 코드량이 많아지는 현상 이것을 State로 제어할 수 있다.
State란 => 화면에 자동적으로 반영하는 함수
export default function Home() {
const = 10; // js영역
return (
<div>{count}</div> // HTML 영역
)
}
const [state, setState] = useState() // state변수에 상태값 설정
const [state, setState] = useState(true); // 참
const [state, setState] = useState(false); // 거짓
[setState] => 바꿔주는 함수
state를 그냥 바꿀 수 없음
// count = state변수 , setCount = state를 바꿔주는 함수
// () 안에 값을 넣으면 그 값은 초기값이 된다.
const [count, setCount] = useState(); // 바꿀수 있음
자동으로 반영되는 것이 아닌 useState 기능을 가지고 있어야 한다.
이전 시간에 만든 게시물 등록 Form이다 이것을 어떤 등록하기를 눌렀을때에 상태값을 조절 할 수 있을까?
// 이메일 비밀번호 타이틀 박스, description 동작을 나눈 state
const [email, setEmail] = useState("");
const [emailError, setEmailError] = useState("");
const [password,setPassword] = useState("");
const [passwordError, setPasswordError] = useState("");
const [title, setTitle] = useState("");
const [titleError, setTitleError] = useState("");
const [description, setDescription] = useState("");
const [descriptionError, setDescriptionError] = useState("");
const [address, setAddress] = useState("");
const [addressError, setAddressError] = useState("");
const [addressDescription, setAddressDescription] = useState("");
const [addressDescriptionError, setAddressDescriptionError] = useState("");
const [youtubeLink, setYoutubeLink] = useState("");
const [youtubeLinkError, setYoutubeLinkError] = useState("");
function EmailChange(event) {
console.log(event.target.value);
setEmail(event.target.value);
}
function PaswwordChange(event) {
setPassword(event.target.value);
}
function TitleChange(event) {
setTitle(event.target.value);
}
function DescriptionChange(event) {
setDescription(event.target.value);
}
function AddressChange(event) {
setAddress(event.target.value);
}
function AddressDescriptionChange(event) {
setAddressDescription(event.target.value);
}
function YoutubeChange(event) {
setYoutubeLink(event.target.value);
}
먼저 회원가입 오류를 반활할 Change 변수와 Error 변수를 useState상태값에 담고 function으로 Change 함수를 선언해
event객체로 value 값을 반환 시켜주었다.
let success = false;
function Signup() {
if(success < !success) {
success = true;
if (email.includes("@") === false) {
setEmailError("이메일에 @를 포함해주세요");
} else if (email.value === "") {
setEmailError("이메일을 입력하세요")
} else {
setEmailError("")
}
if (password.length < 4) {
setPasswordError("비밀번호는 최대 5글자 입니다.")
} else {
setPasswordError("");
}
if (title === "") {
setTitleError("제목을 입력해주세요.");
} else {
setTitleError("");
}
if (title.length < 6) {
setTitleError("제목은 최대 6글자 이상입니다.")
}
if (description.length < 10) {
setDescriptionError("내용은 최대 10줄 이상입니다.");
} else {
setDescriptionError("");
}
if (addressDescription.length < 1) {
setAddressDescriptionError("주소값을 입력해주세요")
} else {
setAddressDescriptionError("");
}
if (youtubeLink.length < 1) {
setYoutubeLinkError("유튜브 링크를 추가하세요")
} else {
setYoutubeLinkError("")
}
if (address === "") {
setAddressError("상세주소")
} else {
setAddressError("")
}
return ;
}
const로 정의한 변수들을 조건문을 사용해서 회원가입시 동작할 sucess로 true 와 false 상태를 나누었다.
if (email || password || description || title || description || addressDescription || youtubeLink === true) {
success = true;
alert("가입")
return true;
} else {
success = false;
return false;
}
};
또한 모든 값들이 정확하게 입력된 true 상태라면 가입완료라는 alert창을 띄워주고 그렇지 않다면 sucess 상태를 false로
바꿔주도록 if문을 사용하였다.
이제 설정한 값들을 배치시키면 되는데
<Wrapper>
<Container>
<Title>게시물 등록</Title>
<Form>
<FirstContainer>
<ContainerTitle>작성자</ContainerTitle>
<FirstInput id="email" type="email" placeholder='이름을 적어주세요' onChange={EmailChange} emailError={emailError}></FirstInput>
<span style={{color:"red"}}>{emailError}</span>
</FirstContainer>
<FirstContainer>
<ContainerTitle>비밀번호</ContainerTitle>
<FirstInput type="password" placeholder='비밀번호를 입력해주세요' onChange={PaswwordChange} passwordError={passwordError}></FirstInput>
<span style={{color:"red"}}>{passwordError}</span>
</FirstContainer>
</Form>
<Form>
<SecondContainer>
<ContainerTitle>제목</ContainerTitle>
<SecondInput placeholder="제목을 입력하세요" onChange={TitleChange} titleError={titleError}></SecondInput>
<span style={{color:"red"}}>{titleError}</span>
</SecondContainer>
</Form>
<Form>
<SecondContainer>
<ContainerTitle>내용</ContainerTitle>
<ThreeInputContainer descriptionError={descriptionError}>
<TableError style={{ paddingBottom:"0px"}} class="border">{descriptionError}</TableError>
<ThreeInput id="red" placeholder='내용을 작성해주세요' onChange={DescriptionChange}>
</ThreeInput>
</ThreeInputContainer>
</SecondContainer>
</Form>
<Form>
<AddressContainer>
<AddressContainerTitle>주소</AddressContainerTitle>
<br/>
<AddressNumberContainer>
<AddressNumberBox type="text" placeholder='07250' onChange={AddressChange} addressError={addressError}></AddressNumberBox>
<AddressNumberBlackBox type="button" placeholder='우편번호 검색' >
우편번호 검색
</AddressNumberBlackBox>
<AddressNumberBoxContainer>
<span>{addressDescriptionError}</span>
<AddressNumber type="url" placeholder='주소' onChange={AddressDescriptionChange} addressDescriptionError={addressDescriptionError} >
</AddressNumber>
<span>{addressDescriptionError}</span>
<AddressNumber type='url' placeholder='상세주소' onChange={AddressDescriptionChange} addressDescriptionError={addressDescriptionError}>
</AddressNumber>
</AddressNumberBoxContainer>
</AddressNumberContainer>
</AddressContainer>
</Form>
<Form>
<YoutubeContainer>
<YoutubeTitle>유튜브</YoutubeTitle>
<span>{youtubeLinkError}</span>
<YoutubeInput type="url" placeholder="링크를 복사해주세요"
onChange={YoutubeChange} youtubeLinkError={youtubeLinkError}
></YoutubeInput>
</YoutubeContainer>
</Form>
<Form>
<PictureContainer>
<PictureTitle>사진첨부</PictureTitle>
<PictureImageContainer>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Picture
>
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[0]);
}}
></Pictureinput>
<PicturePlus
>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
<Picture>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[1]);
}}
></Pictureinput>
<PicturePlus>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
<Picture>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[0]);
}}
></Pictureinput>
<PicturePlus>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
</PictureImageContainer>
</PictureContainer>
</Form>
<Form>
<MainSettingContainer>
<MainSettingTitle>메인 설정</MainSettingTitle>
<MainSettingInput type="radio" name="youtube" seleted></MainSettingInput>유튜브
<MainSettingInput type="radio" name="youtube"></MainSettingInput>사진
</MainSettingContainer>
</Form>
<Form>
<SubmitButton type="button" onClick={Signup}>등록하기</SubmitButton>
</Form>
</Container>
모든 값을 입력할때 등록할 수 있도록 SubmitButton 값에 onclick으로 Signup값을 넣어주고
Input값에 Onchange 변수로 할당해 값이 조건에 맞을때 동작하도록 설정해주었다.
const [imageSrc, setImageSrc] = useState('');
const processImage = (fileBlob) => {
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
return new Promise((resolve) => {
reader.onload = () => {
setImageSrc(reader.result);
resolve();
}
}
)
}
<PictureContainer>
<PictureTitle>사진첨부</PictureTitle>
<PictureImageContainer>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Picture
>
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[0]);
}}
></Pictureinput>
<PicturePlus
>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
<Picture>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[1]);
}}
></Pictureinput>
<PicturePlus>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
<Picture>
{imageSrc && <ImportPicture src={imageSrc} /> }
<Pictureinput
type="file"
placeholder='upload'
name="image"
multiple
onChange={(e) => {
processImage(e.target.files[0]);
}}
></Pictureinput>
<PicturePlus>+</PicturePlus>
<PictureUpload>Upload</PictureUpload>
</Picture>
</PictureImageContainer>
</PictureContainer>
또한 사진을 업로드 하는 기능은 사진 업로드를 담당할 상수를 만들어 주고 new 배열을 사용해 FileReder()에게
사진 업로드 기능을 담당할 생성자를 하나 만들어 주면서 reader에 filBlob 즉 이미지를 받아올 수 있는 기능을 만들어 주었고
Promise 객체를 사용해 reader 변수를 onLoad 불러오게 된다면 ImageSrc 이미지를 받아온 경로의 이미지 값을
내가 import해온 이미지 result(결과)값을 가져오도록 정의하였다.
그 결과 내가 import해온 이미지 결과 값을 가져올 수 있었다.
가입하기 클릭시 상태변화 제어법
Styled-components에서 상태를 제어하는 방법은 아주 간단하다.
먼저 중요한 단계는
1.내가 입력할 Input 창에 값에 따라 오류를 반환하는 것
2.입력할 값에 따라 true와 false를 구분하는 방법
let success = false;
function Signup() {
if(success < !success) {
success = true;
if (email.includes("@") === false) {
setEmailError("이메일에 @를 포함해주세요");
} else if (email.value === "") {
setEmailError("이메일을 입력하세요")
} else {
setEmailError("")
}
if (password.length < 4) {
setPasswordError("비밀번호는 최대 5글자 입니다.")
} else {
setPasswordError("");
}
if (title === "") {
setTitleError("제목을 입력해주세요.");
} else {
setTitleError("");
}
if (title.length < 6) {
setTitleError("제목은 최대 6글자 이상입니다.")
}
if (description.length < 10) {
setDescriptionError("내용은 최대 10줄 이상입니다.");
} else {
setDescriptionError("");
}
if (addressDescription.length < 1) {
setAddressDescriptionError("주소값을 입력해주세요")
} else {
setAddressDescriptionError("");
}
if (youtubeLink.length < 1) {
setYoutubeLinkError("유튜브 링크를 추가하세요")
} else {
setYoutubeLinkError("")
}
if (address === "") {
setAddressError("상세주소")
} else {
setAddressError("")
}
return ;
}
if (email || password || description || title || description || addressDescription || youtubeLink === true) {
success = true;
alert("가입")
return true;
} else {
success = false;
return false;
}
};
먼저 입력한 값에 따라 에러메시지를 포함하는 if문을 정의한다.
<FirstContainer>
<ContainerTitle>작성자</ContainerTitle>
<FirstInput id="email" type="email" placeholder='이름을 적어주세요' onChange={EmailChange} emailError={emailError}></FirstInput>
<span style={{color:"red"}}>{emailError}</span>
</FirstContainer>
<FirstContainer>
<ContainerTitle>비밀번호</ContainerTitle>
<FirstInput type="password" placeholder='비밀번호를 입력해주세요' onChange={PaswwordChange} passwordError={passwordError}></FirstInput>
<span style={{color:"red"}}>{passwordError}</span>
</FirstContainer>
</Form>
<Form>
<SecondContainer>
<ContainerTitle>제목</ContainerTitle>
<SecondInput placeholder="제목을 입력하세요" onChange={TitleChange} titleError={titleError}></SecondInput>
<span style={{color:"red"}}>{titleError}</span>
</SecondContainer>
</Form>
<Form>
<SecondContainer>
<ContainerTitle>내용</ContainerTitle>
<ThreeInputContainer descriptionError={descriptionError}>
<TableError style={{ paddingBottom:"0px"}} class="border">{descriptionError}</TableError>
<ThreeInput id="red" placeholder='내용을 작성해주세요' onChange={DescriptionChange}>
</ThreeInput>
</ThreeInputContainer>
</SecondContainer>
</Form>
2. 내가 입력한 결과에 따라 값이 바뀌도록 onChange에게 Change변수를 할당해주고 에러를 표시할 곳에는
선언한 error 메세지를 표시하도록 선언 해주는것
이렇게 해준다면 내가 erroMessage를 표시하는 컴포넌트에 상태를 제어할 수 있게 된다.
export const FirstInput = styled.input`
width:100%;
max-width: 480px;
height: 52px;
outline:none;
padding-left:10px;
border: ${({emailError,passwordError}) => (emailError || passwordError ? "1px solid red" : "1px solid #000")}
`;
styled에서 이런 것을 제어할 수 있는데 먼저 email을 담당하는 Input에게 상태로 받아온 emailError 값을 가져와서
true와 false 상태를 구분해준다.
border: ${({emailError,passwordError}) => (emailError || passwordError ? "1px solid red" : "1px solid #000")}
이렇게 불러와주면 erorr를 표시할 컴포넌트의 값을 가져올 수 있고 이곳에서 true & false로 비교해
true 일때는 테두리가 red로 false일때는 테두리를 검정색 상태로 정의해주면
입력한 결과 값이 erro 값이 true라면 빨간 테두리를 return 하면서 에러메시지를 보여주고 false라면 아무런 값도 받아오지 않는 현상을 볼 수 있다.
간단하게 설명하지면
로그인 에러를 표시할 기능에는 error 값을 할당하고 그곳에서 onChange 값이 변경될때 기능을 입력할 Input에게
할당한뒤 Styled-components에서 그 props 상태 값을 가져와서 true 일 때와 false일때의 스타일을 정의할 수 있는 것이다.
'etc. > TIL' 카테고리의 다른 글
[TIL] 01월 13일 - 동기 vs 비동기 (0) | 2022.01.15 |
---|---|
2022년 01-12 GraphQL의 시작 (0) | 2022.01.13 |
2022년 01-10일 Next.js TIL.. (0) | 2022.01.10 |