- Day 7 - CSS 작업 (完)2024년 09월 12일 19시 47분 12초에 업로드 된 글입니다.작성자: 삶은고구마
- 누이터 스타일링하기
- 버그 수정
1.누이터 스타일링하기
css를 중점으로 다루는 프로젝트는 아닌만큼 최대한 간결하고 깔끔하게만 설명한듯 하다.
(저자 역시 그렇게 말했다.)
1)package.json 수정
스타일적용을 위해 맨 먼저 할 것은 스타일 관련 패키지를 설치 하는 것이다.
폰트어썸의 아이콘을 사용 (유료,무료 모두있음)
package.json
"dependencies": { "@fortawesome/fontawesome-free":"^5.14.0", "@fortawesome/fontawesome-svg-core":"^1.2.30", "@fortawesome/free-brands-svg-icons":"^5.14.0", "@fortawesome/free-regular-svg-icons":"^5.14.0", "@fortawesome/free-solid-svg-icons":"^5.14.0", "@fortawesome/react-fontawesome":"^0.1.11",
터미널
npm install
2)깃허브 커밋 이력 참고하여 styles.css파일 만들기
style.css 커밋주소
https://vo.la/kBejt
3)styles.css 임포트
src 폴더에 styles.css 파일 만든 후 깃허브 수정 이력을 참고하여
styles.css를 모두 채우고 index.js파일에서 styles.css임포트
index.js
import "./styles.css";
4)Auth,AuthForm 컴포넌트에 스타일 작업
AuthForm.js
return( <> <form onSubmit={onSubmit} className="container"> <input name="email" type = "email" placeholder="Email" required value={email} onChange={onChange} className="authInput" /> <input name="password" type = "password" placeholder="Password" required value={password} onChange={onChange} className="authInput" /> <input type = "submit" value = {newAccount ? "Create Account" : "Log in"} className="authInput authSubmit" /> <br/> {error && <span className="authError">{error}</span>} </form> <span onClick={toggleAccount} className="authSwitch"> {newAccount ? "Sign In" : "Create Account"} </span> </> );
Auth.js
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTwitter,faGoogle,faGithub } from "@fortawesome/free-brands-svg-icons"; .. return( <div className="authContainer"> <FontAwesomeIcon icon={faTwitter} color={"#04AAFF"} size="3x" style={{marginBottom:30}} /> <AuthForm/> <div className="authBtns"> <br/> <button onClick={onSocialClick} name="google" className="authBtn"> Continue with Google <FontAwesomeIcon icon={faGoogle}/></button> <button onClick={onSocialClick} name="github" className="authBtn"> Continue with Github <FontAwesomeIcon icon={faGithub}/></button> </div> </div> );
5)Router 컴포넌트에 스타일 적용
로그인 한 상태에서 적용해보자.
Router.jsreturn( <Router> {isLoggedIn && <Navigation userObj={userObj}/>} <div style={{ maxWidth:890, width:"100%", margin:"0 auto", marginTop:80, display:"flex", justifyContent:"center", }}> <Routes> {isLoggedIn ? ( <> <Route exact path="/" element={<Home userObj={userObj}/>}/> <Route exact path="/profile" element={<Profile refreshUser={refreshUser} userObj={userObj}/>}/> </> ) : ( <Route exact path="/" element={<Auth />}></Route> )} {/* <Route path="*" element={<Navigate replace to="/" />} /> */} </Routes> </div> </Router> );
책에서는 기존 <></>태그를 <div></div>태그로 변경하고 div에 스타일을 적용하라 하는데
그대로 하면 에러가 난다.
이 코드에서 switch를 routes로 바꾼 이유와 같은데,
react-router-dom v6부터는 switch를 사용하지 않는다. 대신 Routes로 사용한다.
Routes의 자식은 Route만 가능.
<Routes>
<Route/>
<Route/>
</Routes>
이런식으로 사용한다.
혹시나 싶어서 Routes안의 div를 외부로 빼내고(윗쪽에) 실행하니 잘 된다.
구글링해보니까 다른 분들도 이렇게 처리했다..ㅠㅠ
해결 및 참조 링크
https://velog.io/@nemo/react-error-routes
[React 에러 일지] ... is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
🚫 \[...] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>react-router-dom v6부터는,Switc
velog.io
실행결과
6)Home 컴포넌트에 스타일 적용
return ( <div className="container"> {/* 기존 폼을 컴포넌트화 함 */} <NweetFactory userObj={userObj}/> <div style={{marginTop:30}}> {nweets.map((nweet)=>( <Nweet key = {nweet.id} nweetObj={nweet} isOwner={nweet.creatorId === userObj.uid} /> ))} </div> </div> );
7)Navigation 컴포넌트 스타일 적용하기
폰트어썸 아이콘 추가
ul 엘리먼트를 flex로 중앙 정렬
프로필은 이름이 있는 경우엔 이름 출력, 아닌 경우엔 Profile만 출력 (삼항연산자)
Navigation.js
return( <nav> <ul style ={{display:"flex", justifyContent:"center",marginTop:50}}> <li> <Link to="/" style = {{marginRight:10}}> <FontAwesomeIcon icon={faTwitter} color={"#04AAFF"} size="2x"/> </Link> </li> <li> <Link to="/profile" style={{ marginLeft:10, display:"flex", msFlexDirection:"column", alignItems:"center", fontSize:12, }} > <FontAwesomeIcon icon={faUser} color={"#04AAFF"} size="2x"/> <span style={{marginTop:10}}> {userObj.displayName ? `${userObj.displayName}의 Profile` :"Profile"} </span> </Link> </li> </ul> </nav> );
8)Nweet,NweetFactory 컴포넌트에 스타일 적용
폼을 담당하는 팩토리부터 수정.
현재는 아무 글자를 입력하지 않아도 트윗을 할 수 있도록 되어있는데 이것을 막는 코드를 추가할 것이다.
NweetFactory.js
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlus, faTimes } from "@fortawesome/free-solid-svg-icons"; .. const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); //트윗을 단 한글자도 하지 않는 경우 전송하지 않음 if(nweet==="") { return; } .. return ( <> <form onSubmit={onSubmit} className="factoryForm"> <div className="factoryInput__container"> <input className="factoryInput__input" value={nweet} onChange={onChange} type="text" placeholder="What's on your mind?" maxLength={120} /> <input type="submit" value="→" className="factoryInput__arrow"/> </div> <label htmlFor="attach-file" className="factoryInput__label"> <span>Add Photos</span> <FontAwesomeIcon icon={faPlus}/> </label> <input id="attach-file" type="file" accept ="image/*" onChange={onFileChange} style={{ opacity:0, }} /> {attachment && ( <div className="factoryForm__attachment"> <img src={attachment} style={{ backgroundImage:attachment, }} /> <div className="factoryForm__clear" onClick={onClaerAttachment}> <span>Remove</span> <FontAwesomeIcon icon={faTimes}/> </div> </div> )} </form> </> );
Nweet.js
import { dbService,storageService } from "fbase"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTrash,faPencilAlt } from "@fortawesome/free-solid-svg-icons"; const Nweet = ({nweetObj,isOwner})=>{ const onDeleteClick = async() =>{ const ok = window.confirm("삭제하시겠습니까?"); // console.log(ok); //확인을 눌렀을 경우.. if(ok) { // console.log(nweetObj.id); await dbService.doc(`nweets/${nweetObj.id}`).delete(); // console.log(data); if(nweetObj.attachmentUrl !=="") await storageService.refFromURL(nweetObj.attachmentUrl).delete(); } } return( <div className="nweet"> <h4>{nweetObj.text}</h4> {nweetObj.attachmentUrl && ( <img src={nweetObj.attachmentUrl} width="100px" height="100px"/> )} {isOwner &&( <div className="nweet__actions"> <span onClick={onDeleteClick}> <FontAwesomeIcon icon={faTrash}/> </span> </div> )} </div> ); }; export default Nweet;
참고로 실제 트위터는 먼저 썼던 트윗을 수정할 순 없고 오로지 삭제만 가능하다.
책에는 수정기능도 있는데 일단 삭제기능만 구현했다. (수정은 추후 클론코딩할 예정)
9)Profile 컴포넌트에 스타일 적용
profile.js
return( <div className="container"> <form onSubmit={onSubmit} className="profileForm"> <input onChange={onChange} type="text" placeholder="display name" value={newDisplayName} autoFocus className="formInput" /> <input type="submit" value="update profile" className="formBtn" style={{ marginTop:10, }} /> </form> <span className="formBtn cancelBtn logOut" onClick={onLogOutClick}> Log Out </span> </div> );
10)브라우저 명 바꾸기
현재 탭 이름은 React App
이것도 수정이 가능하다..!
index.html
<!-- <title>React App</title> --> <title>Nwitter</title> </head>
2.버그 수정
1)트윗 순서가 엉키는 버그
간혹 트윗 순서가 엉키는 현상이 발생..
트윗을 화면에 출력하는 Home 컴포넌트에 문제가 있을 것이다.
이 컴포넌트를 살펴보면 파이어스토어에서 트윗 데이터를 가져 온 다음
화면에 렌더링할 때 트윗 데이터를 가져오기만 할 뿐 정렬은 하지 않았다.
orderby를 적용하자.
생성시간을 내림차순 정렬하였다.
이렇게 하면 최신 글이 맨 위로 올라오고 오래된 글일수록 밑으로 내려간다.
Home.js
//실시간 db 위한 onSnapshot함수적용 useEffect(()=>{ dbService.collection("nweets") .orderBy("createdAt","desc") .onSnapshot((snapshot)=>{ const newArray = snapshot.docs.map((document)=>({ id : document.id,...document.data(), })); setNweets(newArray); }); },[]);
2)사진 선택 취소 버그
이 에러는 사진을 한 번 선택해서 등록했다가 다시 add photo를 눌러서 선택창을 열은 후 취소할 때 발생한다.
ERRORFailed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'. TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'. at onFileChange (http://localhost:3001/nwitter/static/js/bundle.js:634:12) at HTMLUnknownElement.callCallback (http://localhost:3001/nwitter/static/js/bundle.js:72537:18) at Object.invokeGuardedCallbackDev (http://localhost:3001/nwitter/static/js/bundle.js:72581:20) at invokeGuardedCallback (http://localhost:3001/nwitter/static/js/bundle.js:72638:35) at invokeGuardedCallbackAndCatchFirstError (http://localhost:3001/nwitter/static/js/bundle.js:72652:29) at executeDispatch (http://localhost:3001/nwitter/static/js/bundle.js:76795:7) at processDispatchQueueItemsInOrder (http://localhost:3001/nwitter/static/js/bundle.js:76821:11) at processDispatchQueue (http://localhost:3001/nwitter/static/js/bundle.js:76832:9) at dispatchEventsForPlugins (http://localhost:3001/nwitter/static/js/bundle.js:76841:7) at http://localhost:3001/nwitter/static/js/bundle.js:77001:16발생 이유는 파일 첨부가 안 되었을 때 파일을 읽도록 코드가 작성되어있어서 그런 것이다.
파일이 첨부되었을때만 읽도록 수정하면 될것이다. [true상태일때만]
기존에 실행되던 코드를 if문 조건안에만 넣으면 된다.
NweetFactory.js
// reader.readAsDataURL(theFile); //파일이 첨부 안되었을 때는 읽지 않도록 조건을 수정한다. if(Boolean(theFile)) { reader.readAsDataURL(theFile); }
3)수정 된 누이터 배포하기
터미널
npm run deploy
클론코딩 마무리 버전으로 깃허브에 커밋.
+
리액트는 학원 수료할 때 정말 잠깐동안 배운거라 머릿속에 남은게 없었다.
트위터 클론코딩이 끌려서 무작정 시작했다.
아리송했던 리액트 문법이 조금씩 정리되었다.
=
임포트, 컴포넌트 하는것도 자바처럼 원하는 기능을 import하는거고 모듈화 해서 따로 관리하는것이라 생각되었다.
마치 처음 mvc 패턴을 익혔을때가 생각난다.
그 전에는 무작정 한 소스에 다 때려넣었는데 이런식으로 하면 코드 덩치가 비대해지고 유지보수가 굉장히 어려워진다.
실제로 ctrl F로 찾아다니다가 시간을 허비하고...
독립적으로 관리할 수 있는 함수는 따로 관리하는 것이 좋다. 컴포넌트가 이와 같은 기능을 수행하는 것 같다.
=
설치부터 에러나고 react-router-dom 버전이 달라지면서 레거시된 혹은 사라진 문법도 있었다.
다시 책을 꼼꼼히 살펴보고 안되면 구글링을 해봤다.
나처럼 개정판 이후에 누이터 코딩을 하려다가 책과 달라진 리액트 버전에 고생한 사람들의 기록덕에 큰 문제는 없었다.
=
파이어베이스 스토리지를 연동하니 기본적인 회원가입/로그인/데이터 저장기능을 사용할 수 있어서 무척 좋았다.
=
습득한 지식을 다시 복습하고 주중에 시간이 나면 완성된 코드에서 다른 작업을 더 추가할까 싶다.
실제 트위터는 글뿐만이 아니라 이미지 란이 따로 있어서 이미지만 모아서 볼 수있다. 이걸 구현해보고싶다.
'공부 > 클론코딩' 카테고리의 다른 글
Day 7 - 깃허브 작업 (0) 2024.09.12 Day 7 - 누이터 서브 기능 만들기 (1) 2024.09.12 Day 5 - 로그인 기능 구현하기 1 (0) 2024.09.09 Day 4 - 클론 코딩 시작 2 (2) 2024.09.09 Day 3 - 클론 코딩 시작 (0) 2024.09.07 다음글이 없습니다.이전글이 없습니다.댓글