최종 완료된걸 정리한 글은 제일 아래에 있습니다 ^.^
Firebase Storage도 Firebase Database처럼
"Collection/Document/Collection/..."와 같은 폴더 구조와 비슷하다.
post-tweet-form.tsx
import { addDoc, collection } from "firebase/firestore";
import { useState } from "react";
import styled from "styled-components"
import { auth, db, storeage } from "../routes/firebase";
import { ref, uploadBytes } from "firebase/storage";
const Form = styled.form`
display: flex;
flex-direction: column;
gap: 10px;
`;
const TextArea = styled.textarea`
border: 2px solid white;
padding: 20px;
border-radius: 20px;
font-size: 16px;
color: white;
background-color: black;
width: 100%;
resize: none;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
&::placeholder {
font-size: 16px;
}
&:focus {
outline: none;
border-color: #1d9bf0;
}
`;
const AttachFileButton = styled.label`
padding: 10px 0px;
color: #1d9bf0;
text-align: center;
border-radius: 20px;
border: 1px solid #1d9bf0;
font-size: 14px;
font-weight: 600;
cursor: pointer;
`;
const AttachFileInput = styled.input`
display: none;
`;
const SubmitBtn = styled.input`
background-color: #1d9bf0;
color: white;
border: none;
padding: 10px 0px;
border-radius: 20px;
font-size: 16px;
cursor: pointer;
&:hover,
&:active {
opacity: 0.9;
}
`;
export default function PostTweetForm(){
const [isLoading, setLoading] = useState(false);
const [tweet, setTweet] = useState("");
const [file, setFile] = useState<File|null>(null);
const onChange = (e : React.ChangeEvent<HTMLTextAreaElement>) => {
setTweet(e.target.value);
};
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {files} = e.target;
if(files && files.length === 1){
setFile(files[0]);
}
};
const onSubmit = async (e:React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const user = auth.currentUser;
if(!user || tweet === "" || tweet.length > 180){
return;
}
try {
setLoading(true);
const doc = await addDoc(collection(db, "tweets"), {
tweet,
createAt : Date.now(),
username : user.displayName || "Anonymous",
userId : user.uid
});
if(file){
const locationRef = ref(storeage, `tweets/${user.uid}-${user.displayName}/${doc.id}`)
//import { ref } from "firebase/storage";
//tweets 폴더 안에 트윗을 보내는 유저들 저마다의 폴더를 하나씩 생성한다.
await uploadBytes(locationRef, file);
}
} catch (e) {
console.log(e);
}finally{
setLoading(false);
}
};
return <Form onSubmit={onSubmit}>
<TextArea onChange={onChange} value={tweet} placeholder="What is happening?"/>
<AttachFileButton htmlFor="file">{file ? "Photo added" : "Add photo"}</AttachFileButton>
<AttachFileInput onChange={onFileChange} type="file" id="file" accept="image/*"/>
<SubmitBtn type="submit" value={isLoading ? "Posting..." : "Post Tweet"}/>
</Form>
};
firebase.ts
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import {getAuth} from "firebase/auth";
import { getStorage } from "firebase/storage";
import { getFirestore } from "firebase/firestore";
/*
도메인, api ket 등 여러가지 키 값들이 포함된 config 개체를
firebase app 생성 시에 firebaseConfig 객체가 주어졌다.
*/
const firebaseConfig = {
apiKey: "AIzaSyDRHBD9TNQFRsFTV8nf8N2SRJ7C65c4r08",
authDomain: "nwitter-reloaded-68b8c.firebaseapp.com",
projectId: "nwitter-reloaded-68b8c",
storageBucket: "nwitter-reloaded-68b8c.firebasestorage.app",
messagingSenderId: "464085535303",
appId: "1:464085535303:web:ba1b5d7558448f3dc07f62",
measurementId: "G-HF9RX7ZX6F"
};
/*
firebaseConfig 옵션을 통해서 app을 생성한다.
*/
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
/*
app에 대한 인증 서비스를 사용한다.
*/
export const auth = getAuth(app);
/*
데이터베이스와 스토리지에 대한 엑세스 권한을 얻는다.
*/
export const storeage = getStorage(app);
export const db = getFirestore(app);
Firebase 사이트 Console 내 Storage에 tweets 폴더가 생성되었고 tweets 폴더 내에 유저ID와 이름으로 된 폴더가 생성된걸 확인할 수 있다.그리고 해당 폴더 내에는 이미지가 생성된걸 확인할 수 있다.
Firebase 사이트 Console 내 Storage에서 tweets 폴더 내의 폴더를 삭제한다.
* 삭제한 이유는 이미지를 업로드하고 나서 그 이미지의 URL를 받기 위해서이다.
Firebase 사이트 Console 내 Firestore Database 내 Collection을 삭제한다.
* 삭제한 이유는 최종으로 이미지를 업로드하고 나서 그 이미지의 URL를 받는 것을 테스트 하기 위해서이다.
<최총 완료>
데이터를 클라우드 데이터서비스와 스토리지에 업로드하고
다운로드 URL을 사용하여 둘을 연결하였다.
post-tweet-form.tsx
import { addDoc, collection, updateDoc } from "firebase/firestore";
import { useState } from "react";
import styled from "styled-components"
import { auth, db, storeage } from "../routes/firebase";
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
const Form = styled.form`
display: flex;
flex-direction: column;
gap: 10px;
`;
const TextArea = styled.textarea`
border: 2px solid white;
padding: 20px;
border-radius: 20px;
font-size: 16px;
color: white;
background-color: black;
width: 100%;
resize: none;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
&::placeholder {
font-size: 16px;
}
&:focus {
outline: none;
border-color: #1d9bf0;
}
`;
const AttachFileButton = styled.label`
padding: 10px 0px;
color: #1d9bf0;
text-align: center;
border-radius: 20px;
border: 1px solid #1d9bf0;
font-size: 14px;
font-weight: 600;
cursor: pointer;
`;
const AttachFileInput = styled.input`
display: none;
`;
const SubmitBtn = styled.input`
background-color: #1d9bf0;
color: white;
border: none;
padding: 10px 0px;
border-radius: 20px;
font-size: 16px;
cursor: pointer;
&:hover,
&:active {
opacity: 0.9;
}
`;
export default function PostTweetForm(){
const [isLoading, setLoading] = useState(false);
const [tweet, setTweet] = useState("");
const [file, setFile] = useState<File|null>(null);
const onChange = (e : React.ChangeEvent<HTMLTextAreaElement>) => {
setTweet(e.target.value);
};
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {files} = e.target;
if(files && files.length === 1){
setFile(files[0]);
}
};
const onSubmit = async (e:React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const user = auth.currentUser;
if(!user || tweet === "" || tweet.length > 180){
return;
}
try {
setLoading(true);
const doc = await addDoc(collection(db, "tweets"), {
tweet,
createAt : Date.now(),
username : user.displayName || "Anonymous",
userId : user.uid
});
if(file){
const locationRef = ref(storeage, `tweets/${user.uid}-${user.displayName}/${doc.id}`);
const result = await uploadBytes(locationRef, file);
const url = await getDownloadURL(result.ref);
await updateDoc(doc, {photo : url});
}
setTweet("");
setFile(null);
} catch (e) {
console.log(e);
}finally{
setLoading(false);
}
};
return <Form onSubmit={onSubmit}>
<TextArea onChange={onChange} value={tweet} placeholder="What is happening?"/>
<AttachFileButton htmlFor="file">{file ? "Photo added" : "Add photo"}</AttachFileButton>
<AttachFileInput onChange={onFileChange} type="file" id="file" accept="image/*"/>
<SubmitBtn type="submit" value={isLoading ? "Posting..." : "Post Tweet"}/>
</Form>
};
firebase.ts
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import {getAuth} from "firebase/auth";
import { getStorage } from "firebase/storage";
import { getFirestore } from "firebase/firestore";
/*
도메인, api ket 등 여러가지 키 값들이 포함된 config 개체를
firebase app 생성 시에 firebaseConfig 객체가 주어졌다.
*/
const firebaseConfig = {
apiKey: "AIzaSyDRHBD9TNQFRsFTV8nf8N2SRJ7C65c4r08",
authDomain: "nwitter-reloaded-68b8c.firebaseapp.com",
projectId: "nwitter-reloaded-68b8c",
storageBucket: "nwitter-reloaded-68b8c.firebasestorage.app",
messagingSenderId: "464085535303",
appId: "1:464085535303:web:ba1b5d7558448f3dc07f62",
measurementId: "G-HF9RX7ZX6F"
};
/*
firebaseConfig 옵션을 통해서 app을 생성한다.
*/
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
/*
app에 대한 인증 서비스를 사용한다.
*/
export const auth = getAuth(app);
/*
데이터베이스와 스토리지에 대한 엑세스 권한을 얻는다.
*/
export const storeage = getStorage(app);
export const db = getFirestore(app);
nwitter-reloaded.z01
19.53MB
nwitter-reloaded.z02
19.53MB
nwitter-reloaded.z03
19.53MB
nwitter-reloaded.z04
19.53MB
nwitter-reloaded.z05
19.53MB
nwitter-reloaded.zip
15.59MB
'트위터(React, TypeScript, Firebase, Vite)' 카테고리의 다른 글
17. TWEETING - Realtime [1] (0) | 2025.04.17 |
---|---|
16. TWEETING - Fetching Timeline (0) | 2025.04.17 |
14. Tweeting to Firestore (0) | 2025.04.16 |
13. TWEEING - Post Tweet Form (0) | 2025.04.15 |
12. TWEETING - Navigation Bar (0) | 2025.04.15 |