const user = auth.currentUser;
{user?.displayName ? user.displayName : "Anonymous"}
user의 이름이 있을경우에는 출력해주고 없을경우엔 Anonymous 를 출력해준다
해당 if문은 user.displayName이 중복으로 쓰이는데
{user?.displayName ?? "Anonymous"}
이런식으로 표현이 가능하다.
const [avatar, setAvatar] = useState(user?.photoURL);
useState로 사용자 이미지의 URL을 가져온다.
{Boolean(avatar) ? <AvatarImg src={avatar} /> : <UserLogo src="/user-logo.svg" />}
Boolean을 사용해서 유저가 등록한 이미지가 있는지 확인하기
이미지가 없을 시 기본값이 되는 svg 이미지를 출력하고 이미지가 있으면 사용자의 프로필 사진을 출력한다.
<AvatarInput id="avatar" type="file" accept="image/*" />
AvatarUpload를 눌럿을때 숨어있는 input을 노출하기위해 id값 부여하기
<AvatarUpload htmlFor="avatar">
input의 id값을 htmlFor="" 를 이용해서 연결해주기
const onAvatarChange = (e:React.ChangeEvent<HTMLInputElement>) => {};
<AvatarInput onChange={onAvatarChange} id="avatar" type="file" accept="image/*" />
input을 통해 사진을 업로드시 바로 적용하기위해 ChangeEvent 생성해주기
input에 onChange속성 적용하기
const {files} = e.target;
if(files && files.length === 1){
const file = files[0];
}
파일은 한개만 필요하기때문에 if문으로 검증하기
const locationRef = ref(storage, );
파이어베이스에서 strorage instace 넘겨주기
const locationRef = ref(storage, `avatars/${user?.uid}`);
이후 avatars 폴더안에 유저이미지가 저장되고 파일명은 유저의 아이디가 된다.
유저가 유저이미지를 변경해도 동일한 파일이름으로 업로드가 되어서 덮어쓰기가 진행된다.
const result = await uploadBytes(locationRef, file);
locationRef로 경로를 설정해주고 file을 넣어준다.
const avatarUrl = await getDownloadURL(result.ref);
getDownload를 사용하여 해당 URL을 result로 가져온다.
await updateProfile(user, {
photoURL : avatarUrl,
})
유저의 프로필을 업데이트
위와같이 작성하면 Argument of type 'User | null' 이 뜬다 user가 null값일수도 있다는 뜻
해당부분을 검증하기위해 위에서 유저가 null인지 검증을 해준다
유저이미지가 state에 할당되어있기 때문에 setAvatar 작성
const fetchTweets = async () => {};
useEffect(() => {
fetchTweets();
}, []);
realtime으로 작성하지 않기위해서 useEffect에서 fetchTweets를 실행
const fetchTweets = async () => {
const tweetQuery = query(collection(db, "tweets"))
};
fetchTweets에 query 만들어주기
데이터베이스 인스턴스를 전달, 컬렉션 이름
collection(db, "tweets"),
where("userId", "==", user?.uid),
orderBy("createdAt", "desc"),
limit(25)
조건에 맞는 글만 가져오기위해 필터링하기 tweets의 구조를 reference
where(db 아이디값 , 조건, 로그인아이디값)
createdAt의 값으로 역순정렬
최대 25개만 출력되게끔 설정
const snapshot = await getDocs(tweetQuery);
document가져와서 query 전달해주기
getDocs는 query snapshot을 반환해준다.
const tweets = snapshot.docs.map(doc => {})
원하는 내용을 가져오기 위해서 map 함수를 사용
const tweets = snapshot.docs.map(doc => {
const {tweet, createdAt, userId, username, photo} = doc.data();
})
무슨 데이터를 가져올지 컬럼명 명시해주기
const [tweets, setTweet] = useState<ITweet[]>([]);
useState를 사용해서 사용하고 배열로 호출되는 인터페이스를 갖게하고 빈 배열로 시작하게 한다.
<Tweets>
{tweets.map(tweet => <Tweet key={tweet.id} {...tweet} />)}
</Tweets>
map으로 tweets 데이터를 가져와 tweet 출력하기
위 코드까지 작성하면 만들려는 쿼리에 인덱스가 필요하다고 오류가 뜬다.
Uncaught (in promise) FirebaseError: The query requires an index. You can create it here:
Firestore에 어떤종류의 필터를 사용할지 알려줘야한다.
해당오류의 url을 누르면 index를 설정해줄 수 있는 firebase 사이트로 이동한다.
자동으로 설정이 되어있는데 저장해주면 된다.
index 생성이 완료되면 해당 문구를 볼 수 있다.
생성이 완료되면 오류가 뜨지 않고 정상적으로 페이지가 출력된다.
프로필 페이지에서 닉네임을 변경하기 challenge
const [editing, setEditing] = useState(false);
const [newUsername, setNewUsername] = useState(user?.displayName);
const onUpdate = () => {
setEditing((prev) => !prev);
};
닉네임을 받는 username과 버튼의 상태를 변경하는 editing을 useState로 선언한다
const onUsernameChange = (event : React.ChangeEvent<HTMLInputElement>) => {
const {
target : {value},
} = event ;
setNewUsername(value);
};
Input의 value값을 이벤트로 잡아준다
const onSubmit = async(event : React.FormEvent) => {
event.preventDefault();
if (!user) return;
if(user?.displayName !== newUsername){
await updateProfile(user, {
displayName : newUsername
});
}
}
form태그의 onsubmit을 preventDefault로 초기화 시켜주고
현재 유저의 닉네임과 입력한 닉네임이 다를경우에 db에 저장시켜준다
if(!user)로 유저정보를 검증하고 null값일경우 return
{editing ?
<>
<NameForm onSubmit={onSubmit}>
<UserInput type="text" value={newUsername} onChange={onUsernameChange} />
<ButtonBox>
<SubmitButton type="submit" value="Update" />
<UpdateButton onClick={onUpdate}>Cancel</UpdateButton>
</ButtonBox>
</NameForm>
</>
:
<>
<Name>{user?.displayName ?? "Anonymous"}</Name>
<UpdateButton onClick={onUpdate}>Rename</UpdateButton>
</>
}
onClick 이벤트를 editing의 상태로 조건을 걸어서 Rename버튼을 클릭하면 input태그를 노출하여 변경할 수 있게한다.