- URL: https://devtown-lovat.vercel.app/
- 계정
ID
: devtown@test.comPassWord
: 123456
😈 데브타운은 developer's town의 약자로, 더 나은 개발자로서 성장하기 위한 주니어 개발자들의 커뮤니티 플랫폼입니다.
💪🏻 개발자들이 모여 다양한 정보를 자유롭게 공유하면서 함께 성장 할 수 있는 환경을 제공합니다.
🥰 주니어로서 겪을 수 있는 문제들에 대해 서로 공유하고 공감하며 같이 해결해 나갈 수 있습니다.
📚 개발 관련 상품을 판매할 수 있습니다.
- 공통 컴포넌트 제작 (버튼, alert창)
- 게시물 업로드
- 게시물 상세 확인
- 댓글 작성
- 잘못된 경로일 경우 404 에러 페이지 라우팅
- 유저 검색
- 공통 컴포넌트 제작 (nav, modal창)
- 데브 타운 아이디어 기획 및 아이콘 디자인
- 개인 프로필 페이지
- 상대 프로필 페이지
- 팔로우 / 언팔로우 기능 구현
- 프로필 수정
- nav 및 모달창 구현
- 공통 컴포넌트 제작 (Auth 인풋, 탭 메뉴, 검색창, 팔로우, 이미지포스트, 상품창)
- 로그인
- 회원가입
- 프로필 설정
- 토큰 검증
- 홈 피드리스트
- 판매 상품 등록, 수정, 삭제
📦 src
┣ 📂 assets
┣ 📂 pages
┃ 📂 redux
┃ ┣ 📂 actions
┃ ┣ 📂 reducers
┃ ┗ 📜 store.js
┣ 📂 components
┃ ┣ 📂 common
┃ ┃ ┣ 📂 alert
┃ ┃ ┣ 📂 button
┃ ┃ ┣ 📂 HomeImgPost
┃ ┃ ┣ 📂 modal
┃ ┃ ┣ 📂 nav
┃ ┃ ┣ 📂 PostInDetail
┃ ┃ ┣ 📂 product
┃ ┃ ┣ 📂 search
┃ ┃ ┣ 📂 tabMenu
┃ ┃ ┗ 📂 textActiveInput
┃ ┣ 📂 error
┃ ┣ 📂 home
┃ ┃ ┣ 📂 HomeFeed
┃ ┃ ┗ 📂 HomeNoFollower
┃ ┣ 📂 join
┃ ┃ ┣ 📂 JoinMemberShip
┃ ┃ ┗ 📂 ProfileSetting
┃ ┣ 📂 list
┃ ┃ ┣ 📂 chatList
┃ ┃ ┗ 📂 followList
┃ ┣ 📂 information
┃ ┃ ┣ 📂 InformationCard
┃ ┃ ┗ 📂 InformationContainer
┃ ┣ 📂 join
┃ ┃ ┣ 📂 JoinMember
┃ ┃ ┗ 📂 JoinProfile
┃ ┣ 📂 login
┃ ┃ ┣ 📂 LoginMain
┃ ┃ ┣ 📂 LoginOptions
┃ ┃ ┗ 📂 Splash
┃ ┣ 📂 post
┃ ┣ 📂 product
┃ ┃ ┣ 📂 AddProduct
┃ ┃ ┗ 📂 ProductModification
┃ ┗ 📂 profile
┃ ┣ 📂 userPost
┃ ┣ 📂 userProduct
┃ ┗ 📂 userProfile
┣ 📜 App.jsx
┗ 📜 index.js
0. splash | 1. 회원가입 |
---|---|
2. 프로필 설정 | 3. 로그인 |
---|---|
4. 홈화면 | 5. 계정 검색 |
---|---|
6. 채팅 | 7. 내 프로필 |
---|---|
8. 내 프로필 수정 | 9. 상품 등록 |
---|---|
10. 내 상품 수정 | 11. 내 상품 삭제 |
---|---|
12. 내 상품 링크 이동 | 13.팔로우, 팔로워 |
---|---|
14. 게시글 등록 | 15. 게시글 댓글 등록 |
---|---|
16. 상대 프로필 | 17. 상대 게시글 신고 |
---|---|
18. 상대 상품 클릭시 링크 이동 | 19. 설정 및 로그아웃 |
---|---|
- 팀원 모두 리덕스를 잘 모르는 상태에서 사용하려다보니, 리덕스에 대해 공부하고 적용하기까지 시간이 많이 소요되었습니다.
- Live Share와 게더타운을 이용한 페어프로그래밍을 통해, 리덕스를 적용하여 코드를 작성하였습니다.
- 이해한 내용을 주석으로 작성하여, 리덕스 활용시 쉽게 이해할 수 있도록 하였습니다.
- Redux-Persist를 사용해 새로고침이나 웹을 종료해도 store가 리셋되는 것을 방지하고 싶었지만
적용을 해도 계속해서 데이터가 휘발되는 이슈가 있었습니다.
- 또한 특정한 reducer에만 데이터를 사용하고 싶은 경우 persistConfig에 whitelist를 설정해 넣어줬지만
데이터가 휘발되어 현재는 Redux-Persist를 사용하지 않고 sessionStorage를 통해 새로고침을 방지하고 있습니다.
이미지 프리뷰가 뜨면 서버로 전송이 안되고, 반대로 서버로 전송이 되면 프리뷰가 보이지 않음.
-
이미지를 서버에 업로드하는 액션과 포스트하는 액션을 따로 설정
=> 오히려 코드의 복잡도가 올라가고 그다지 효율적이지 않아 postAction 이라는 하나의 함수로 dispatch 설정
-
useEffect 안에서 formData와 fileReader를 동시에 만들어서 감시하도록 했지만 useEffect 코드를 제거하고 formData 관련 코드를 리덕스의 액션함수 파일로 이동
function UploadPage() {
const [postText, setPostText] = useState('');
const [uploadedImg, setUploadedImg] = useState([]);
const [imgPreview, setImgPreview] = useState([]);
//중략
const onSubmitHandler = (e) => {
e.preventDefault();
if (uploadedImg === [] && postText === '') {
alert('게시물을 작성해주세요');
} else {
history.push('/myprofile');
dispatch(postAction.post(uploadedImg, postText));
}
};
const HandlePostText = (e) => {
setPostText(e.target.value);
};
const HandleOnchange = (e) => {
let prevImgs = [];
for (let index = 0; index < e.target.files.length; index++) {
const element = e.target.files[index];
const reader = new FileReader(); //file을 비동기로 읽기 위한 FileReader 객체를 생성
reader.onload = function () {
//FileReader가 이미지를 잘 인코딩하고 난 후의 결과를 처리
prevImgs.push(reader.result);
setImgPreview((prev) => {
if (prevImgs.length <= 3) {
let newPreviews = prevImgs;
return newPreviews;
} else {
return prev;
}
});
};
reader.readAsDataURL(element);
}
setUploadedImg((prev) => {
if (prev.length < 3) {
let newList = e.target.files;
return newList;
} else {
alert('이미지는 최대 3장까지 업로드가 가능합니다.');
return prev;
}
});
};
}
// 이전 코드: 이미지 미리보기 로직과 이미지 url 만드는 로직을 useEffect 안에다 작성하여 코드가 제대로 동작이 안됨.
// useEffect(() => {
// for (let index = 0; index < uploadedImg.length; index++) {
// const file = uploadedImg[index];
// const reader = new FileReader();
// reader.onloadend = () => {
// SetImgPreview((prev) => {
// let newPreviews = [...prev, reader.result];
// if (newPreviews.length > 3){
// alert('이미지는 최대 3장까지 업로드가 가능합니다.')
// newPreviews = newPreviews.slice(0,3)
// return newPreviews
// }
// return newPreviews;
// });
// };
// reader.readAsDataURL(file);
// const formData = new FormData()
// formData.append('image',file)
// }
// }, [uploadedImg]);
//후략
function post(fileList, postText) {
const formData = new FormData();
if (fileList.length > 0) {
for (let index = 0; index < fileList.length; index++) {
const file = fileList[index];
formData.append('image', file);
}
//formData 처리 후 이미지업로드 요청
return async (dispatch, getState) => {
let url = 'https://mandarin.api.weniv.co.kr';
const reqPath = '/image/uploadfiles';
const token = getState().auth.token;
try {
let imageUrls = ''
if (fileList.length >0){
let fileRes = await fetch(url + reqPath, {
method: 'POST',
body: formData,
});
const fileJson = await fileRes.json();
imageUrls = fileJson
.map((fileData) => url + '/' + fileData.filename)
.join(',');
}
//게시물 포스트 요청
const postReq = '/post';
let postRes = await fetch(url + postReq, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-type': 'application/json',
},
body: JSON.stringify({
post: {
content: postText,
image: imageUrls,
},
}),
});
//후략
1. 아직 구현하지 못한 기능
- 채팅 기능
- 신고하기 기능
- 게시물 수정 및 삭제
- 상품/게시물 수정시 리렌더링
- Redux-Persist
2. 컴포넌트의 아토믹화
3. 중복되는 코드를 없애고 커스텀훅 모듈 제작
4. 리렌더링 최소화