유투브에서 찾은 인스타그램 클론 코딩을 해보았다. (react, tailwindcss, firebase)
firebase를 리액트에서 연동하여 데이터베이스 및 서버를 구축하고 클라이언트를 구현하는 영상이라, firebase를 처음으로 이용해보게 되었다.
firebase
구글에서 만든 모바일 및 웹 애플리케이션 개발 플랫폼으로, 굉장히 간단하게 애플리케이션을 위한 서버를 사용할 수 있다. authentication, database 등 애플리케이션 구현에 필요한 최소한의 필요를 가장 간편하게 만족시켜줄 수 있는 방법 중 하나이다. 파이어베이스는 작은 프로젝트에서 백엔드를 구현하지 않고 아이디어를 시험해본다거나, 프로토타입을 테스트해보는 경우에 적합하다. 다만 큰 프로젝트를 구현할 때는 백엔드를 직접 구현하여 사용하는 것이 올바른 방법이다.
위 설명처럼 쉽게 데이터베이스와 서버를 사용할 수 있어서 좋았다!
파일구조와 인상깊은 부분에 대한 설명만 간단히 기록한다.
기본 index, App , seed(db 연동)아래
App에서 Login, SignUp, Profile, Dashboard, NotFound 페이지를 라우팅해준다
pages - 페이지 구성
==============================
components - 필요한 컴포넌트들
==============================
constants - 이동할 주소들 변수로 저장
===============================
styles
why-did-you-render
date-fns : js의 new Data() 이용 다양한 포맷의 날짜 표현 가능 (ex) 포스트 아래에 5 days ago)
date-fns_메인페이지에 사용예시prop-types : React 컴포넌트의 타입 체크
react-loader-spinner : 화면이 로딩 중일때, 돌아가는 애니메이션을 손쉽게 구현할 수 있다(!)
사용예시react-loading-skeleton : 화면이 로딩 중일때, 그 화면의 틀을 미리 보여준다.
사용예시postcss : CSS 전처리기이며, CSS 작성을 더 편하게 만들어주는 javascript 도구들(Plugins)
다양한 플러그인과, 플러그인을 추가할 수 있는 환경을 제공(postcss 자체는 아무일도 하지 않음)autoprefixer : -webkit- 등의 prefix 없이 스타일을 지정할 수 있다.(대표적인 potcss의 플로그인)
prettier : 코드를 더 이쁘게 만든다. ESLint와 가장 큰 차이점은 코드 품질에 관련된 것은 다루지 않는 다는 점이다. Prettier는 단지 일관적인 스타일로 코드를 다듬을 뿐이다.
eslint : ECMAScript 코드에서 문법적 오류나 더 나은 코드로 정정하는 린트 도구 이다. 즉, 코드의 가독성을 높이고 오류와 버그를 제거하여 단단한 코드를 만드는 것이 목적이다.
date-fns, react-loader-spinner 는 UI디자인시 유용하게 쓸 수 있을 것 같다!
seed
서버가 시작될 때 애플리케이션이 가지고 있어야 할 정적인 데이터들을 DB에 추가해주는 기능
React.lazy
코드분할- 앱을 "지연 로딩"하게 도와주고 사용자들에게 획기적인 성능 향상을 하게 해준다. 앱의 코드 양을 줄이지 않고도 사용자가 필요하지 않은 코드를 불러오지 않게 하며 앱의 초기화 로딩에 필요한 비용을 줄여준다.
event.preventDefault()
cancels the event if it is cancelable, meaning that the default action that belongs to the event will not occur. For example, this can be useful when: Clicking on a "Submit" button, prevent it from submitting a form.
특정 부분에만 웹 브라우저의 기본 동작을 막을 수 있음
ex) submit 버튼 눌렀을때 페이지가 리프레쉬 되는 것 방지
설명페이지
useContext
React Context API provides the ability to pass props to child components without needing to pass them down manually.
컴포넌트 간의 끝없는 props 내려받기를 방지
트리에 createContext를 생성, Provider로 자식 컴포넌트들을 감싸주면 자식들은 모두 읽을 수 있다.
공식문서
localStorage
클라이언트 단, 즉 브라우저 상에 데이터를 저장할 수 있는 기술
여러 탭이나 창 간에 데이터가 서로 공유되며 탭이나 창을 닫아도 데이터는 브라우저에 그대로 남아 있다.(같은 컴,같은 브라우저에서)
브라우저의 콘솔 창을 열고 테스트 가능
설명참고_페이지
firebase 연결한 파일들 import 해서 해당 컴포넌트, 페이지들과 연결
useEffect, async를 많이 쓴다.
비동기적으로 서버(데이터베이스)와 소통하며 정보들을 받아오기 위해?
< Routing에서의 lazy,suspense 및 기타 표현 >
import { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import ReactLoader from './components/loader';
import * as ROUTES from './constants/routes';
import UserContext from './context/user';
import useAuthListener from './hooks/use-auth-listener';
import ProtectedRoute from './helpers/protected-route';
const Login = lazy(() => import('./pages/login'));
const SignUp = lazy(() => import('./pages/sign-up'));
const Dashboard = lazy(() => import('./pages/dashboard'));
const Profile = lazy(() => import('./pages/profile'));
const NotFound = lazy(() => import('./pages/not-found'));
export default function App() {
const { user } = useAuthListener();
return (
<UserContext.Provider value={{ user }}>
<Router>
<Suspense fallback={<ReactLoader />}>
<Switch>
<Route path={ROUTES.LOGIN} component={Login} />
<Route path={ROUTES.SIGN_UP} component={SignUp} />
<Route path={ROUTES.PROFILE} component={Profile} />
<ProtectedRoute user={user} path={ROUTES.DASHBOARD} exact>
<Dashboard />
</ProtectedRoute>
<Route component={NotFound} />
</Switch>
</Suspense>
</Router>
</UserContext.Provider>
);
}
lazy 컴포넌트는 Suspense 컴포넌트 안에 위치해야 한다.
fallback prop에는 로딩화면으로 띄울 페이지를 표시할 수 있다.
또, Suspense 컴포넌트 하나로 여러개의 lazy 컴포넌트를 감쌀 수 있어서 편리하다!(동일 로딩효과 여러 컴포넌트에 적용 가능)
< sort와 dateCreated를 이용 날짜순으로 최신 사진 정리 가능 >
import { useState, useEffect } from 'react';
import { getPhotos } from '../services/firebase';
export default function usePhotos(user) {
const [photos, setPhotos] = useState(null);
useEffect(() => {
async function getTimelinePhotos() {
// does the user actually follow people?
if (user?.following?.length > 0) {
const followedUserPhotos = await getPhotos(user.userId, user.following);
// re-arrange array to be newest photos first by dateCreated
followedUserPhotos.sort((a, b) => b.dateCreated - a.dateCreated);
setPhotos(followedUserPhotos);
}
}
getTimelinePhotos();
}, [user?.userId, user?.following]);
return { photos };
}
< 코멘트 갯수 3개 넘어가면 3개까지만 목록 보여주고, 더 보기 버튼을 누를시의 로직 >
export default function Comments({ docId, comments: allComments, posted, commentInput }) {
const [comments, setComments] = useState(allComments); // 모든 코멘트들 상태로 저장
const [commentsSlice, setCommentsSlice] = useState(3); // 코멘트 갯수 상태로 저장(기본 3)
const showNextComments = () => {
setCommentsSlice(commentsSlice + 3);
};
return (
<>
<div className="p-4 pt-1 pb-4">
{comments.slice(0, commentsSlice).map((item) => (
<p key={`${item.comment}-${item.displayName}`} className="mb-1">
<Link to={`/p/${item.displayName}`}>
<span className="mr-1 font-bold">{item.displayName}</span> // 해당 유저 이름 클릭시 링크
</Link>
<span>{item.comment}</span>
</p>
))}
{comments.length >= 3 && commentsSlice < comments.length && (
<button
className="text-sm text-gray-base mb-1 cursor-pointer focus:outline-none"
type="button"
onClick={showNextComments}
onKeyDown={(event) => {
if (event.key === 'Enter') {
showNextComments();
}
}}
>
View more comments
</button>
)}
.
.
.
인스타그램 like 수에 따른 숫자, 아이콘 변화와
toggleLiked로 좋아요와 취소 기능 (toggledLiked에 따라 like갯수 증감)
follow,follower 기능도 흥미로웠다
{error && <p className="mb-4 text-xs text-red-primary">{error}</p>}
&& 활용 문법 : error가 있을시 뒤에 것을 렌더링
<추가>