배포된 월드컵 사이트 보러가기
월드컵 프로젝트는 유튜브 침착맨님의 월드컵 컨텐츠를 보고 영감을 받아 제작하였다.
월드컵 페이지를 제작한다면 사용자가 자바스크립트 메서드 월드컵을 참여해서 사용자의 통계를 내고 개발을 처음 배우는 분들이 통계를 참고하여 많이 쓰이는 메서드에 대해서 공부하면 어떨까 하는 생각에 만들어 보게 되었다.
또한 위코드 부트캠프와 원티드 프리 온보딩 과정을 수료하면서 코딩에 대한 자신감에 생긴 나는 무엇이든 만들 수 있을 것 같다는 자신감과 함께 뽐내고 싶었다.
원티드 프리 온보딩 과정에서 팀원 이셨던 프론트엔드 개발자 영길님 에게 토이프로젝트에 대한 구성을 설명 드렸고 영길님이 같이 하시길 원하셔서 프론트엔드 두 명에서 개발을 하게 되었다.
월드컵 사이트는 하나의 주제에 16개의 항목이 존재하고 16강부터 결승까지 두 개의 항목을 나열한 뒤 사용자가 선택한 요소가 상위 라운드로 진출하는 시스템입니다.
월드컵 참여를 완료하거나 랭킹 보기를 눌렀을 때 사용자들이 선택한 요소들을 집계한 통계 페이지 이동합니다. 각 항목의 우승 비율, 라운드 승률을 오름차순, 내림차순으로 정렬이 가능하며 댓글 창을 통해 해당 통계에 대한 사용자들의 생각을 볼 수도 있습니다.
로그인 기능도 구현되어 있으며 로그인된 유저일 시 댓글 입력 , 월드컵 생성 , 마이페이지에서 자신이 생성한 월드컵 삭제 등의 기능 또한 제공됩니다.
모바일 사용자들 또한 있을 거라 생각하여 미디어 쿼리를 사용한 모바일 화면도 구현했습니다.
2022.03.09 - 2022.03.16
프론트엔드 개발자 2명
React , TypeScript , styled-component , Json-Server , Axios , GitHub
[ { "title": 월드컵 이름, "creatorId": 생성자 아이디, "id": 월드컵 아이디, "count": 월드컵 진행 횟수, "createdAt": 월드컵 생성 시간, "list": [ { "id": 월드컵 요소 아이디, "candidate": 월드컵 요소 이름, "roundWin": 라운드 승리 횟수, "roundLose": 라운드 패배 횟수, "champion": 우승 횟수 }, . . . ], "comments": [ { "id": 댓글의 아이디, "text": 댓글 내용, "createdAt": 댓글 작성 시간, "creatorId": 댓글 작성자 아이디, "userId": 작성자 이름 }, . . . ], }, ]
단순하게 레이아웃만 설계하고 시작한 이유는 레이아웃으로 보여주는 방식에 따라 구현 해야 하는 로직이 달라질 수 있다고 생각되어 현업에서 많이 쓰이는 피그마를 사용하여 세세하게 디자인적인 요소는 제외하고 대강의 틀을 잡아놓고 시작하게 되었습니다.
모바일 화면은 초기 단계에서 설계하지 않고 먼저 데스크톱 사이트를 기본으로 구현하고 사용자에게 적합한 모바일 화면을 미디어 쿼리를 사용하여 구현했습니다.
개발 진행 상황 공유를 위해 트렐로를 이용하였습니다. 트렐로를 사용함으로써 원만한 소통을 통해 협업을 진행했습니다.
const shuffleArray = (array: IWorldCupItemProps[]) => { for (let i = 0; i < array.length; i++) { let j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; };
noData ? ( <NoData>😢 진행된 적 없는 월드컵입니다.</NoData> ) : ( <> <Ranking /> <Wrapper> <CommentWrapper> <CommentForm userObj={userObj} /> </CommentWrapper> </Wrapper> </> )}
const onDeleteClick = async (id: string) => { const ok = window.confirm('정말 삭제하시겠습니까?'); if (ok) { const findIndex = data.comments.findIndex( (item: IWorldCupCommentProps) => item.id === id ); const newComments = [ ...data.comments.slice(0, findIndex), ...data.comments.slice(findIndex + 1), ]; await axios .put(`${BASE_URL}/world/${keyword[2]}`, { ...data, comments: newComments, }) .then(() => setRefetch((prev) => !prev)); } };
월드컵이 진행될 때 만약 랜덤으로 배열의 순서를 배치하지 않는다면 제대로 된 통계가 나올 수 없는 문제점이 있었습니다.
ex) 월드컵 리스트 전체에서 원래대로라면 1등을 할 A 항목과 2등을 할 B 항목이 있다. 하지만 이 항목은 월드컵 리스트 1 , 2번째에 고정으로 배치되어 있다면 B 항목은 A 항목에 패배하는 경우가 많아 원래라면 2등이지만 상위권에 위치하는 것은 불가능할 것이다.
하지만 배열의 항목들의 순서를 random 메서드를 사용하여 섞어 준다면 예와 같은 상황이 계속 유지되지 않기 때문에 제대로 된 통계가 나오게 됩니다.
월드컵의 우승 비율 통계를 각 요소의 우승 횟수를 기준으로만 정렬한다면 통계가 보기 좋게 나오지 않는 문제점이 있었습니다.
ex) 우승 비율이 같은 항목이 있다면 해당 항목 안에서 순위를 정할 때 라운드 승률이 높은 항목이 상위권에 위치 하는것이 맞지만 우승 횟수만을 기준으로 불가능하다
해결책으로 우승 비율을 정렬한다면 라운드 승률을 기준으로 먼저 정렬을 한 데이터를 우승 비율로 다시 한번 정렬 하는 방법을 사용했습니다.
라운드 승률 또한 우승 비율을 먼저 정렬 후 라운드 승률을 기준으로 정렬했습니다.
모바일 화면에서 헤더 부분의 홈 버튼, 월드컵 생성 , 나의 월드컵 , 로그인 탭을 모두 담기에는 화면의 길이가 부족한 문제가 있었습니다.
해당 문제는 미디어 쿼리를 사용해서 월드컵 만들기 메뉴바 안에 홈 , 월드컵 생성 , 나의 월드컵 탭을 포함하는 방식으로 해결했습니다. 메뉴바는 좌측에서 우측으로 나오는 모달 형식이며 바깥 부분을 누르면 메뉴바가 닫히도록 구현했습니다.
Mock-up 데이터를 사용하여 개발 할 때에는 몰랐지만 json-server를 통해 서버로부터 데이터를 받아오도록 구현 했을 때 서버로 부터 데이터를 받아오는데 시간이 걸려 몇몇의 페이지에서 데이터가 뿌려지지 않은 화면이 출력되는 문제가 있었습니다.
해당 문제는 로딩 스피너 컴포넌트를 따로 제작하여 각 컴포넌트마다 isLoading state 값을 생성하여 데이터를 받아오는 useEffect 함수 첫 부분에서 isLoading state 값에 true를 할당하여 로딩 중, 그리고 데이터를 다 받아온 시점에서 isLoading state 값에 false를 할당하여 로딩 완료를 상태 값으로 지정하여 isLoading state 값이 true 일 때만 삼항 연산자를 통해 로딩 컴포넌트를 화면에 출력하도록 했습니다.
로딩 스피너는 css의 keyframses 와 animation을 사용하여 구현했으며 코드는 이러합니다.
const ModalContent = styled.div<` position: absolute; transform: translate(-50%, -50%); top: 50%; left: 50%; `; const rotate360 = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } `; const Spinner = styled.div` animation: ${rotate360} 0.5s linear infinite; transform: translateZ(0); border-top: 4px solid white; border-right: 4px solid white; border-bottom: 4px solid #424874; border-left: 4px solid #424874; background: transparent; width: 40px; height: 40px; border-radius: 50%; `;
월드컵이 모두 진행된 후 put 메서드를 통해 월드컵 참여를 통해 기존의 월드컵 데이터에서 추가로 적용된 데이터를 바꾸는 데에 있어서 많이 해맸었습니다...
아마도 get , post 와 같이 데이터를 주고받는 건 익숙했지만 바꾸는 건 처음이어서 헤맸던 것 같았습니다...
하지만 결국 해결했습니다~!~!~!
useEffect(() => { if (winner.length === 1) { toggleModal(); axios .put(`${BASE_URL}/world/${keyword}`, { ...data, list: data.list, count: (data.count += 1), }) .then((res) => console.log('성공')); } }, [winner]);
코드처럼 기존의 데이터를 스프레드 문법으로 이용하며 수정할 값의 데이터를 입력하여 객체 형식의 데이터로 만든 후 put 메서드를 사용해서 기존의 데이터를 수정했습니다.
해당 이미지 처럼 netlify로 배포를 했을 때 에러가 뜨는 현상이 있었습니다.
원인은 netlify에 Redirect설정을 하지 않았기 때문에 처음에 사이트를 어디로 Redirect시킬 것인지 모르기 때문입니다.
참고 자료 링크
해당 에러는 Build command 에 CI=npm run build 를 사용하여 해결 했습니다.
참고 자료 링크
프론트엔드 개발자가 되겠다고 결심했을 때 나의 생각을 코드를 통해 하나의 웹사이트로 완성하고 싶었던 목표가 있었다
지금까지 내가 진행한 프로젝트는 물론 내가 생각한 걸 구현 하지 못했던 건 아니지만 위코드 부트캠프, 원티드 프리 온보딩 과정을 수료하는 과정의 일부로서 프로젝트를 진행했다
하지만 이번 프로젝트는 모든 과정을 수료하고 정말 그저 내가 평소에 갖고 있던 생각을 웹사이트로 만들고 싶어서 시작했다.
프론트엔드 공부를 시작하고 이번 월드컵 프로젝트를 통해 개발을 시작했을 때의 목표를 이룰 수 있었다.
이번 월드컵 프로젝트는 지금까지 해온 프로젝트 중 가장 완성도가 뛰어나다고 생각한다
특히 사용자가 사이트를 이용하는 흐름에 있어서 잘 짜여져있다고 생각한다
사용자가 월드컵을 참여하고
참여한 월드컵들로 쌓인 데이터를 통계로 나타내었고
그런 통계를 보며 사용자들끼리 의견을 나눌 수 있는 댓글 창을 만들었고
사용자가 월드컵을 생성하여 통계와 유저들의 의견을 접할 수 있게 개발했다.
이것을 가능하게 하기 위해서 서버가 필요했다
프론트엔드 개발자 두명 뿐이었지만 json-server 라이브러리를 통해 간단하게 필요한 REST-API를 구축했다.
낯설었기에 어려웠지만 소중한 경험이 된 것 같다..!
이제는 배포한 것 에서 끝나지 않고 사이트를 한번 운영해보려고 한다
지금으로서는 문제점이 없어 보이지만 유저들이 사용하면서 미처 개발 중에 생각하지 못했던 부분이 있다면 수정을 해서 사이트를 유지보수 해나가 볼 것이다
또 해보고 싶은 것이 있는데 월드컵 중에 라면 월드컵이 있다
통계가 어느 정도 쌓인다면 라면 월드컵의 통계와 시장의 라면 판매순위를 비교하여 우리 사이트가 얼마나 유의미한 통계를 내는지도 비교 해보고 싶다.
토이프로젝트를 며칠 동안 밤새워가면서 진행했지만 시간 가는 줄 몰랐고 정말 재밌게 한 것 같다
과정이 쉽지만은 않았지만 완성된 월드컵 프로젝트를 보기만 해도 너무 뿌듯하다!
얼마나 뿌듯하냐면 배포까지 끝낸 후 페이지를 두 세 시간 동안 만지작거렸던 것 같다...
내가 만들었지만 정말 너무나도 잘 만들었다는 생각에 잠겨 계속해서 만져보았다...
다음에도 좋은 아이디어가 떠오르면 또 토이프로젝트를 해보고 싶다!!
끝!
잘 보고 갑니다 😀 정말 열심히 만드신 게 눈에 보여요!😫