Skip to content

SoEasyTeam/Dev.Town

Repository files navigation

주니어 개발자들의 커뮤니티 데브타운

멋쟁이사자처럼 프론트엔드스쿨 2기 TEAM 참쉽조잉🤷‍♀️

[배포 URL]

스크린샷 2022-07-30 오후 3 56 20

📍 프로젝트 소개

😈 데브타운은 developer's town의 약자로, 더 나은 개발자로서 성장하기 위한 주니어 개발자들의 커뮤니티 플랫폼입니다.

💪🏻 개발자들이 모여 다양한 정보를 자유롭게 공유하면서 함께 성장 할 수 있는 환경을 제공합니다.

🥰 주니어로서 겪을 수 있는 문제들에 대해 서로 공유하고 공감하며 같이 해결해 나갈 수 있습니다.

📚 개발 관련 상품을 판매할 수 있습니다.

팀원 소개 및 역할 분담

조다희 임다현 추경훈
blog: daheejo
github: daheejo
blog: Dayhun
github: Dayhun
blog: chuhoon
github: chuhoon
FrontEnd FrontEnd FrontEnd

👩🏻‍💻 조다희 - 팀리더 및 문서화

  • 공통 컴포넌트 제작 (버튼, alert창)
  • 게시물 업로드
  • 게시물 상세 확인
  • 댓글 작성
  • 잘못된 경로일 경우 404 에러 페이지 라우팅
  • 유저 검색

👩🏻‍💻 임다현 - 프로젝트 기획 및 디자인

  • 공통 컴포넌트 제작 (nav, modal창)
  • 데브 타운 아이디어 기획 및 아이콘 디자인
  • 개인 프로필 페이지
  • 상대 프로필 페이지
  • 팔로우 / 언팔로우 기능 구현
  • 프로필 수정
  • nav 및 모달창 구현

🧑🏻‍💻 추경훈 - API 통신 및 Redux 환경 구축

  • 공통 컴포넌트 제작 (Auth 인풋, 탭 메뉴, 검색창, 팔로우, 이미지포스트, 상품창)
  • 로그인
  • 회원가입
  • 프로필 설정
  • 토큰 검증
  • 홈 피드리스트
  • 판매 상품 등록, 수정, 삭제

⚙️ 개발 환경

[기술]

- Front-End

- Back-End: 제공된 API 사용
- Deployment: Heroku

📁 파일 구조

📦 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. 설정 및 로그아웃

⚠️ 개발하며 겪은 이슈

Redux 및 Redux-Persist 적용 이슈

- 팀원 모두 리덕스를 잘 모르는 상태에서 사용하려다보니, 리덕스에 대해 공부하고 적용하기까지 시간이 많이 소요되었습니다.
- Live Share와 게더타운을 이용한 페어프로그래밍을 통해, 리덕스를 적용하여 코드를 작성하였습니다.
- 이해한 내용을 주석으로 작성하여, 리덕스 활용시 쉽게 이해할 수 있도록 하였습니다.
- Redux-Persist를 사용해 새로고침이나 웹을 종료해도 store가 리셋되는 것을 방지하고 싶었지만
  적용을 해도 계속해서 데이터가 휘발되는 이슈가 있었습니다.
- 또한 특정한 reducer에만 데이터를 사용하고 싶은 경우 persistConfig에 whitelist를 설정해 넣어줬지만
  데이터가 휘발되어 현재는 Redux-Persist를 사용하지 않고 sessionStorage를 통해 새로고침을 방지하고 있습니다.
스크린샷 2022-07-30 오후 11 29 07

트러블슈팅; 게시물 업로드 기능에서 이미지 미리보기와 이미지 url서버 전송

[문제]

이미지 프리뷰가 뜨면 서버로 전송이 안되고, 반대로 서버로 전송이 되면 프리뷰가 보이지 않음.

[해결 과정]

  • 이미지를 서버에 업로드하는 액션과 포스트하는 액션을 따로 설정

    => 오히려 코드의 복잡도가 올라가고 그다지 효율적이지 않아 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. 리렌더링 최소화

About

주니어 개발자들을 위한 플랫폼 Dev Town 입니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published