지난 정리에 이어 추가로 js로 변경한 부분을 정리하겠다.
인증 파트가 끝나고 비밀번호를 변경하는 부분을 숙제로 내줬다.
필자는 아래처럼 작성했다.
import { sendPasswordResetEmail } from "firebase/auth";
import { Form, Input, Title, Wrapper, Error } from "../components/auth-components";
import { useNavigate } from "react-router-dom";
import { useState } from "react";
import { auth } from "../firebase";
export default function FindPwd() {
const navigate = useNavigate();
const [email, setEmail] = useState('');
const [isLoading, setLoading] = useState(false);
const [error, setError] = useState('');
const onChange = (e) => {
if(e.target.name === 'email') {
setEmail(e.target.value);
}
};
const onSubmit = async(e) => {
e.preventDefault();
if(isLoading || email === "") return;
try {
setLoading(true);
await sendPasswordResetEmail(auth, email).then(() => {
alert("입력한 이메일로 비밀번호 재설정 메일을 보냈습니다.");
navigate("/login");
});
} catch(e) {
setError(e.message);
} finally {
setLoading(false);
}
};
return (
<Wrapper>
<Title>비밀번호 찾기</Title>
<Form onSubmit={onSubmit}>
<Input
onChange={onChange}
name="email"
value={email}
placeholder="Email"
type="email"
required
/>
<Input
type="submit"
value={isLoading ? "Loading..." : "비밀번호 찾기"}
/>
</Form>
{error !== "" ? <Error>{error}</Error> : null}
</Wrapper>
);
}
인증 파트 다음은 트윗 파트인데 파트가 마무리되기 전에 업로드할 이미지 용량을 제한하는 챌린지를 내주셨다.
console.log를 활용해서 이미지의 용량이 어디에 표시되는지 찾아내면 쉽게 해결할 수 있었다.
필자는 onFileChange라는 메서드를 아래 사진처럼 수정했다.
timeline을 만들면서 복잡한 코드가 꽤 많이 등장했다.우선, 사진과 같이 ITweet이라는 interface를 만들고 이 interface를 아래 사진처럼 tweets 변수에 값으로 사용했다.
전부 TS 문법이다.
트윗이 화면에 게시되는 과정은 잘 알고있듯 DB로부터 데이터를 받아서 화면에 받아온 데이터를 표현해주는 방식이다.
DB를 조금이라도 알고있다면 트윗 한 개를 표현하는데 여러 개의 데이터가 필요하다는 사실을 알 것이다.
즉, 위에 interface는 트윗 하나에 들어있는 데이터들의 타입을 지정해준 것이다.
JS는 TS와 달리 타입지정이 없기 때문에 interface를 만들지 않고 useState 또한 타입을 설정하지 않았다.
필자는 다음 사진처럼 한 줄로 코드를 작성했다.그리고 아래 return쪽에 map함수 사용에서도 작은 에러가 있었다.
강의는 아래 사진처럼 <Tweet key={tweet.id} {...tweet} />으로 작성했는데 이게 TS 문법과 JS 문법의 차이 때문인건지 아니면 필자만 문제가 발생했던 건지는 자세히 모르겠다.어찌됐건 저렇게 똑같이 따라가면 tweets.map is not a function이라는 에러메세지를 보내준다.
map함수가 객체(object)에서는 사용불가하고 배열(array)에서만 사용가능하다는데 확인결과 그런 문제는 아니었고 아래처럼 {...tweet} 대신 {tweet}으로 변경해서 해결하였다.이런 변경점으로 인해 다음 컴포넌트인 tweet.jsx 역시 많은 변화가 생겼다.
아래는 timeline.jsx의 전체코드이다.
import { useState, useEffect } from 'react';
import { styled } from 'styled-components';
import { query, collection, orderBy, getDocs } from 'firebase/firestore';
import { db } from '../firebase';
import Tweet from './tweet';
const Wrapper = styled.div``;
export default function Timeline() {
const [tweets, setTweet] = useState([]);
const fetchTweets = async() => {
const tweetsQuery = query(
collection(db, "tweets"),
orderBy("createdAt", "desc")
);
const snapshot = await getDocs(tweetsQuery);
const tweets = snapshot.docs.map((doc) => {
const {tweet, createdAt, userId, username, photo} = doc.data();
return {
tweet,
createdAt,
userId,
username,
photo,
id: doc.id,
};
});
setTweet(tweets);
};
useEffect(() => {
fetchTweets();
}, [])
return (
<Wrapper>
{tweets.map((tweet) => (
<Tweet key={tweet.id} tweet={tweet} />
))}
{/* {JSON.stringify(tweets)} */}
{/* {tweets.length} */}
{/* {tweets} */}
</Wrapper>
);
}
앞서 작성했듯 timeline.jsx 내에 map함수 사용 시 변경한 부분때문에 tweet.jsx 역시 변경점이 꽤 생겼다.
강의 코드는 다음과 같다.설명에 앞서 강의에서 사용한 {...tweet}은 전개연산자를 사용해 tweet 내에 데이터를 전개한 것이다.
따라서, Tweet 컴포넌트에서 전개된 데이터들을 받아 사용한 것이다.
전개연산자에 대한 더 자세한 설명을 위해 아래 링크를 달아두겠다.
React에서의 전개연산자
배열(map, filter, slice, concat, spread 연산자)
이 전개연산자는 TS뿐만 아니라 JS에서도 사용가능하다.
근데 필자는 timeline.jsx에서 작성한 전개연산 방식에서 에러가 발생하였기에 전개를 하지않고 통째로 데이터를 넘겨주는 방식을 선택하였다.
따라서, 필자의 tweet.jsx는 다음 사진처럼 작성되었다.보면 데이터를 tweet에 통째로 받아왔기에 Tweet에서 tweet이라는 변수로 넘겨주는 데이터를 받고, 아래에 출력하는 과정에서 받아온 데이터의 하위 데이터들을 출력했다.
tweet.xxx와 같은 방식으로 하위데이터에 접근이 가능하다.
아래 Tweet.propTypes~~가 있는데 저건 ESLint로부터 발생한 에러이다.
(따라서, 굳이 작성하지 않아도 페이지 렌더링에서는 문제가 없다!!)
일단, tweet.jsx의 전체코드를 첨부하고 그 아래에 eslint 에러와 관련한 설명을 작성하겠다.
import { styled } from "styled-components";
import PropTypes from "prop-types";
const Wrapper = styled.div`
display: grid;
grid-template-columns: 3fr 1fr;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 15px;
`;
const Column = styled.div``;
const Photo = styled.img`
width: 100px;
height: 100px;
border-radius: 15px;
`;
const Username = styled.span`
font-weight: 600;
font-size: 15px;
`;
const Payload = styled.p`
margin: 10px 0px;
font-size: 18px;
`;
export default function Tweet({ tweet }) {
return (
<Wrapper>
<Column>
<Username>{tweet.username}</Username>
<Payload>{tweet.tweet}</Payload>
</Column>
{tweet.photo ? (
<Column>
<Photo src={tweet.photo} />
</Column>
) : null}
</Wrapper>
);
}
Tweet.propTypes = {
tweet: PropTypes.object.isRequired
}
Disallow missing props validation in a React component definition (react/prop-types)
이 에러를 본 사람이 필자말고도 더 있을거라 생각한다.
위에서 언급했지만 굳이 해결하지 않아도 페이지 렌더링에는 문제가 없다.
아마도 에러라기보다는 경고에 가까운 것 같다.
일단 이 에러가 왜 발생했는지부터 알아야할텐데 아마도 필자가 최근에 vscode extension 중에 ESLint를 설치했기에 발생한 것 같다.
ESLint 확장은 vscode에서 코딩 시에 문법에 오류가 있는 부분을 알려주는 도구라고 하여 설치했는데 덕분에 이런 에러를 마주하게 됐다.
아래 ESLint와 관련한 내용을 참조설정해두니 확장 설치를 할지 말지는 직접 확인해보고 판단하시면 되겠다.
(1) ESLint와 Prettier가 무엇이며 왜 필요하고 어떻게 사용하는지
일단 결론적으로, TS문법에서 진행한 것처럼 미리 받아오는 tweet값의 타입을 지정해주지 않아서 생기는 문제이다.
문제 해결은 두 가지로 나뉘는 것 같다.
1번은 아무리 살펴봐도 stackoverflow에 나온 react/prop-types 설정 부분을 찾을 수가 없었다.
그래서, 위에 작성한 코드처럼 타입을 설정해주는 것으로 경고를 해결하였다.
prop type과 관련해서 어느정도 참고했던 블로그도 같이 참조링크 걸어두겠다.
[React] missing in props validation 뭔데?