새소식

React ·JS·TS

React/TS study - twitter clone(4)

  • -
  return (
    <Wrapper>
      <Menu>
        <Link to="/">
          <MenuItem>
           
          </MenuItem>
        </Link>
        <Link to="/profile">
          <MenuItem>
           
          </MenuItem>
        </Link>
        <MenuItem onClick={onLogOut} className="log-out">
         
        </MenuItem>
      </Menu>
      <Outlet />
    </Wrapper>
  );

 

Link to="/경로" 를 통해서 버튼 아이콘을 클릭했을 때 페이지 이동을 발생시킨다

로그아웃은 따로 경로를 잡을 필요가 없다.

 

class선택자를 선언 할 땐 className="log-out" 로 선언한다.

 

const MenuItem = styled.div`
    cursor: pointer;
    &.log-out {
        color: tomato;
        border-color: tomato;
        svg {
            fill: black;
        }        
    }

className으로 선언한 선택자를 css 속성 적용시 해당 문법을 사용하여 넣는다.

 

주의할 점으로는 &  사이에 공백이 있어서는 안된다. 

 

const Wrapper = styled.div`
    display: grid;
    grid-template-columns: 1fr 4fr;
`;

Wrapper에는 grid를 적용시켜서 작성하였다.

 

 

 

https://studiomeal.com/archives/533

 

이번에야말로 CSS Grid를 익혀보자

이 포스트에는 실제 코드가 적용된 부분들이 있으므로, 해당 기능을 잘 지원하는 최신 웹 브라우저로 보시는게 좋습니다. (대충 인터넷 익스플로러로만 안보면 된다는 이야기) 이 튜토리얼은 “

studiomeal.com

https://developer.mozilla.org/ko/docs/Learn/CSS/CSS_layout/Grids

 

그리드 - Web 개발 학습하기 | MDN

CSS 그리드 레이아웃은 웹페이지를 위한 이차원 레이아웃 시스템입니다. 이 기능을 통해 콘텐츠를 행과 열에 배치할 수 있으며 복잡한 레이아웃을 직접 직관적으로 구축할 수 있는 많은 기능이

developer.mozilla.org

css grid 에대한 설명 및 공식문서

 

 

 

 const navigate = useNavigate();
    const onLogOut = async () => {
        const ok = confirm("Are you sure you want to log out?");
        if(ok){
            await auth.signOut();
            navigate("/login");
        }
    }

로그아웃 버튼을 클릭했을때 유저에게 로그아웃 여부를 묻고 

True를 반환받으면 로그아웃을 실행하고 로그인페이지로 이동시킨다.

 

 

 

 

home에 포스팅할 부분에 대한 파일을 따로 생성해준다.

        <AttachFileButton htmlFor="file">Add photo</AttachFileButton>
        <AttachFileInput id="file" accept="image/*" />

AttachFileInput 에 id값을 부여하고 accept로 모든이미지를 등록할 수 있게한다.

AttachFileButton은 input에대한 label값으로 id값을 통해 연결시켜준다.

값을 가져올 땐 htmlFor="id값"

 

 

 


const TextArea = styled.textarea`
    width: 100%;
    resize: none;
    &::placeholder{
        font-size: 16px;
    }

 resize : none을 통해서 textarea의 크기조절 기능을 없애고 

속성값을 선택할 땐  &::placeholder 를 통해 선택해 css를 적용한다.

 

 

 

    &:focus{
        outline: none;
        border-color: #1d9bf0;
    }

focus 되었을 때에 대한 속성값을 적용시킬땐  &:focus  로 적용시킨다.

 

 

 

    const [isLoading, setLoading] = useState(false);
    const [tweet, setTweet] = useState("");
    const [file, setFile] = useState<File|null>(null);

로딩여부와 글내용, 파일에 대한 값을 확인

 useState<File|null>(null);  는 TS문법, File or null 기본값은 null

 

 

<SubmitBtn type="submit" value={isLoading ? "Posting..." : "Post Tweet"} />

글 작성버튼을 눌렀을 때 로딩여부로 텍스트 출력

 

 

 

    const onChange = (e:React.ChangeEvent<HTMLTextAreaElement>) => {
        setTweet(e.target.value);
    };

onchange 이벤트는 input 태그의 포커스를 벗어났을때 (즉, 입력이 끝났을때) 발생하는 이벤트

 

 

 

    const onFileChange = (e:React.ChangeEvent<HTMLInputElement>) => {
        const {files} = e?.target;
        if (files && files.length === 1) {
            setFile(files[0]);
        }
    }

타입이 file인 input이 변경될 때마다 파일의 배열을 받는다.

e.target 에 files이 존재하고 배열의 길이가 1일때만 그 첫번쨰 파일을 file state에 저장한다.

 

 

<TextArea rows={5} maxLength={200}

TextArea의 높이와 최대 글자길이 값을 지정해준다.

 

 

 

 

firebase에서 DB를 생성해준다.

 

 

이후 Storage로 이동

 

 

 

import { getStorage } from "firebase/storage";
import { getFirestore } from 'firebase/firestore';
export const storage = getStorage(app);
export const db = getFirestore(app);

스토리지와 데이터베이스에 직접 액세스 하기위해서 작성

 

 

firebase에서 db에 수동으로 데이터를 넣을때 작성

 

 

    const onSubmit = (e:React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        if(loading || tweet === "" || tweet.length > 180) return;
    }

loading중이거나 글이 비어있고, 180자가 넘으면 함수를 종료시킨다.

 

    return <Form onSubmit={onSubmit}>

form에 onSubmit을 적용시켜준다.

 

 

 

        try {
            setLoading(true);
            await addDoc( )
        } catch (e) {
            console.log(e);
        } finally {
            setLoading(false);
        }

Firebase SDK에 포함된 addDoc() 함수를 사용

==> 새로운 document를 생성해준다.

 

 

 

    await addDoc(collection( ))

어떤 컬렉션에 document를 생성할지 지정해주기.

 

 

    await addDoc(collection(db,  ))

collection함수에는 Firebase 인스턴스 필요, 이전에 만들어둔 getFirestore() 를 사용

 

 

    await addDoc(collection(db, "tweets"))

이후 컬렉션 이름 명시해주기

 

 

        const user = auth.currentUser

auth를 불러와서 currentUser로 현재 사용자를 가져오기

 

 

            await addDoc(collection(db, "tweets"),{
                tweet,
                craetedAt : Date.now(),
                username : user.displayName || "Anonymous",
                userId : user.uid
            })

useState로 가져온 tweet 값을 가져오고

현재 시간을 글을 작성할때 담아준다

유저 이름을 user.displayName으로 가져오거나 익명으로 표시

userId를 user.uid로 가져와서 글 삭제권한을 필터링 할 수 있게한다.

 

 

FirebaseError: Missing or insufficient permissions.

코드를 작성하여 업로드를 진행하였더니 해당 오류가 발생하였다.

 

 

해결방법 : 

allow read, write : if false; 를

allow read, write : if true; 로 변경해준다.

 

 

<TextArea required

required 속성을 부여해서 글 작성시 글을 필수적으로 적어야 하게 한다.

 

 

 

import { ref } from "firebase/storage";
            if(file){
                const loactionRef = ref(storage)
            }

이전에 작성한 getStorage()를 가져오기

파일의 위치에대한 reference를 받기

업로드된 파일이 저장되는 폴더명과 파일명을 지정

 

 

const loactionRef = ref(storage, `tweets/${user.uid}`)

tweets폴더를 생성하고 그 아래에 user id값으로 폴더를 생성한다( firebase에서 제공하는 uid 사용 )

 

 

            const doc = await addDoc(collection(db, "tweets"),{

await addDoc  을 doc으로 선언하여

 

                const loactionRef = ref(storage, `tweets/${user.uid}-${user.displayName}/${doc.id}`);

업로드되는 파일경로를 tweets/유저id-유저이름/문서id 로 설정해준다.

 

                await uploadBytes(loactionRef, file);

uploadBytes()를 통해서 ( 경로, 파일 ) 설정해주기

uploadBytes()는 promise를 반환한다.

 

                const result = await uploadBytes(loactionRef, file);

result로 선언하여 file의 url값을 가져온다.

 

 

getDownloadURL(result.ref);

firebase/storage에서 getDownloadURL() 가져오기 

result  의 public URL을 반환한다.

 

 

            const doc = await addDoc(collection(db, "tweets"),{
                await updateDoc(doc,{
                    photo : url
                });

기존에 위에서 선언한 doc 데이터에 업데이트 하고싶은 데이터(url)를 넘겨준다.

 

 

            setTweet("");
            setFile(null);

try문이 끝나면 데이터가 firebase db에 저장되기 때문에 input태그들을 리셋 시켜준다.

 

 

 

        if (files && files.length === 1) {
            // setFile(files[0]);
            const selectFile = files[0];
            if(selectFile.size > (1024*1024*1)){
                alert("1MB 이하의 파일을 등록해주세요");
                setFile(null);
            } else {
                setFile(selectFile);
            }
        }

업로드 파일의 크기를 1MB로 제한하기위해 위와같은 코드를 작성하였다.

 

'React ·JS·TS' 카테고리의 다른 글

React/TS study - twitter clone(6)  (0) 2023.10.31
React/TS study - twitter clone(5)  (0) 2023.10.27
React/TS study - twitter clone(3)  (1) 2023.10.24
React/TS study - twitter clone(2)  (0) 2023.10.24
React/TS study - twitter clone(1)  (0) 2023.10.23
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.