1차 프로젝트: 스테이 포레스트 회고록

1. Introduce

스테이폴리오 클론코딩 프로젝트

클론코딩: 개발에만 집중할 수 있도록 기존 웹의 기획과 디자인을 참고하여 하나의 사이트를 완성해보는 프로젝트

스테이 폴리오는 ‘파인 스테이 큐레이션 플랫폼’ 이다. 좋은 질의 숙소를 소개해주어 소수의 여행가들만 누리던 서비스를 많은 여행가가 이용할 수 있도록 숙소 호스트와 고객을 연결해주는 서비스이다.

  • 프로젝트 목표: foundation에서 배운 내용들을 활용해보기
  • 진행기간: 2022년 03월 28일 ~ 2022년 04월 15일(2주 개발 / 1주 리팩토링과 회고)
  • sixsense 팀: Fullstack 5명 (김수빈, 유다송, 이정민, 임근홍, 전하은)

Teck stack

  • Front-end: React.js, React hook, Sass, Router, Git&GitHub
  • Back-end: Express, Prisma, Mysql, Bcypt, JWT, Git&GitHub

2. Why?

첫번째 프로젝트의 대상으로 스테이폴리오를 선택한 이유

이번 프로젝트에서 처음 알게된 서비스이다. 여행이라는 컨텐츠에도 큰 관심이 없다. 그럼에도 스테이폴리오를 선택한 이유는 예쁜 화면이었다. 팀원의 절반 이상이 프론트엔드 직군을 마음에 두고 있었기 때문에 예쁜 화면이 선택에 있어 결정적인 요소였다. 개인적으로는 다양한 슬라이드를 만들어 보고 싶었어서, 슬라이드가 다양한 스테이폴리오를 클론코딩하면 좋을 것 같아 선택했다.

3. What i do

프론트 [listpage 담당]

  • listpage 여행지/숙소 검색, 인원, 가격범위, 스테이유형, 리셋버튼 구현
  • listpage 숙소 리스트
  • listpage 스테이 유형 카테고리 선택시 query 파라미터로 전달 후 해당하는 숙소리스트 받기
  • listpage api 연결
  • Main page api연결

프론트 기능구현 설명

  • react.js로 공통 컴포넌트(검은 버튼) 만들어 재사용, prop으로 onClick event 부여.
  • 인원, 가격범위, 스테이 유형 버튼 공통된 요소를 포함한 컴포넌트 작성, prop으로 부모와 자식 간 데이터 주고 받게 함.
  • 인원, 가격범위, 스테이유형 클릭시 뜨는 작은 하위 창의 유무(visibility: 'hidden'/'visible')와 동시에 2개 이상 뜨지 못하도록 로직 작성하여 state로 관리.
  • useState로 인원, 가격범위, 스테이유형 버튼의 input 값, 버튼 text 관리.
  • fetch, then으로 숙소 리스트 API 연결 및 비동기로 받은 response 데이터를 useState 사용해 상태 관리.
  • 스테이유형 체크박스 클릭후 적용하기 버튼 누를 시 스테이 유형이 query parameter에 추가, useNavigate로 query가 포함된 url로 변경.
  • useLocation().search로 현재 URL에서 query parameter를 가져와서 체크된 유형 숙소리스트 GET 메소드 URI 작성해 request.
  • map 메소드를 활용하여 API에서 받은 json 데이터 렌더링.
  • 리셋버튼 클릭시 useNavigate로 원래의 list 페이지로 이동.
  • useEffect와 dependency로 렌더링 타이밍 관리.

  • dormitory 전체 가져오는 List API
  • dormitory image 가져오는 API
  • city 리스트 가져오는 API
  • rooms Image 가져오는 API

백 기능구현 설명

  • dbdiagram 이용하여 테이블 관계형으로 설계.
  • prisma를 사용하여 schema.prisma의 테이블 작성 및 migration.
  • layered patten 적용해 controller, service, model로 구분.
  • DB의 자원을 기준으로 users, dormitories, rooms로 URI 구분.
  • GET method로 숙소 전체 리스트, 이미지 전체, city 전체, 방 이미지 전체 가져오는 api 작성.
  • statusCode: 서버문제(500) / Ok(200).
  • modele단에서 쿼리문 작성하여 service, controller로 차례대로 넘김.

4. 회고

동기: 성장과 책임감

프로젝트에서 가장 기대한 것은 '성장'이었다. Foundation기간에도 그전에는 상상도 못할 엄청난 성장을 하긴 했으나, 진짜를 만들어보는 건 처음이니 그 과정에서 비교하기 힘든 성장을 할 것이라 기대했다. 성장에 대한 기대는 새로운 것과 어려운 것을 도전할 수 있게 했다. 그러나 프로젝트를 끝까지 해내게 한 것에는 팀프로젝트라는데서 오는 책임감팀원들분들의 열정, 그리고 데드라인이었다.

scrum으로 진행해서 아침 10시 30분에 하는 회의때 하루에 할 수 있는 적정량과 전체 계획을 고려해 내 계획을 말하고, 내가 한 계획을 지키기 위해서 열심히 개발하고, 오후 6시에 '아침에 말한 바를 다 해내진 못했지만 내일 오전 정도면 다 할 수 있을 것 같다.'라고 정확히 내 상황을 알리는 것 자체가 굉장히 동기부여가 많이 되었다. 내가 속한 팀 자체가 의사소통이 활발하며 상황공유가 빨랐고, 가감없이 문제상황을 알렸기 덕분에 나도 부끄러움 없이 내가 못한것, 내가 해낸것, 모르는 것 등등을 물어볼 수 있었다. 이렇게 의견공유가 자유롭고 개발에 열정있는 분들이 있는 회사에 들어가면 정말 편할 것 같다는 생각이 든다.

식스센스 팀의 팀워크를 보여주는 노션 한번 만 봐주십쇼!

고민: 좋은 코드란?

내 담당 api가 일찍 끝나서, main page에 프론트와 백을 연결하는 부분을 맡았다. 여행지/숙소 옆 모달같이 다른 분이 개발하신 컴포넌트를 재사용하기도 했다. 그러다보니 다른 분들의 코드를 읽고 이해하고 연결이 쉽도록 손보는 일을 해볼 수 있었다. 자연스럽게 좋은 코드란 무엇인지에 대해서 고민하게 되었고 나름 정의를 내려보기도 했다.

  1. SoC(관심사 분리)
    SoC가 좋다,좋다 말만 들었지 정말로 왜 좋은지는 몰랐는데 이번 기회에 알게 되었다. 한 함수에 한 가지의 기능만 있거나, 같은 기능을 하는 함수끼리 묶여있으면 읽는 사람이 자연스럽게 내용을 분리해서 읽게 된다. 슬라이드 로직 부분은 이 함수에 있고, 슬라이드 데이터를 받는 부분은 이곳이구나, 혹은 컴포넌트 재사용시 이 부분은 기능에 핵심이니 건들지말고 이 부분은 커스텀해도 되겠구나 하는 이해가 시작되면 재사용이 쉬워지고 보수하기도 쉬워진다.
  2. 변수, 함수이름이 명확함
    프로그래밍 언어 안에서 사람의 언어로 표현할 수 있는 부분은 변수나 함수의 이름같다. 명확한 이름을 가지고 있으면 확실히 이해가 쉬워진다.

반성: 아쉬움

이번 프로젝트가 끝나고보니 가장 후회가 되는 것은 프로젝트의 목표자체가 프론트엔드에 치우쳤다는 점이다. 프론트쪽에서는 다양한 도전을 했지만, 백은 전체를 조회하는 정도 이상을 만들어보지 못한게 아쉽다. 예약기능이나, 별점기능, 하트버튼 기능 등을 구현 못한 것이 아쉽다.

공식문서를 읽지 않고 시작한 것도 아쉽다. 읽어보고 충분히 공부하고 들어갔으면 개발이 좀 편했을 hook들이 있었다. 개발하는 동안에는 너무 정신이 없어서 공부할 시간이 없었지만 지금은 필요성을 느꼈으니 조금씩 어디서 사용하는지 정도는 공부해둬야겠다.

기억하고 싶은 것들

  1. 농담으로 프로그래밍하다보니 도파민에 중독되어서 프로그래밍을 하지 않으면 불안하다는 말을 했다. 하루종일 고민했던 문제가 한 줄의 코드로 해결되고 행복하고... 그런 경험들이 계속 앉아있는 이유가 된다. 짜릿했던 순간들 잊고 싶지 않다.
  2. 팀원분들과 함께 하면서 나에 대해 알아갔다. 팀프로젝트 자체가 처음은 아니나, 이렇게 자주 소통해야하고 상황을 계속 알리고 나의 일과 다른 사람의 일을 구분없이 도우는 팀프로젝트는 처음이었다. 그 과정에서, 나는 책임감이 강한 사람이고, 데드라인에 예민한 편이고, 내가 맡는 일의 양을 고려하기보다는 전체의 완성이 더 중요한 사람이라는 것을 알게 되었다. 그리고 다른 사람에게 도움 줄 수 있는 것을 기뻐한다는 것도 새삼 다시 알게되었다.
  3. 급속성장. 아직은 아무것도 모르는 상태이니 작은 성장도 뚜렷하게 결과물로 보인다. 하지만 점점 더 경험치가 쌓이면 성장에서 재미를 못볼 것이다. 그래도 항상 타인이 아닌 이전의 나와 비교하며 재미있게, 오랫동안 이 일을 하고 싶다.

5. 기억에 남는 코드

랜덤으로 담당페이지를 정하게 되어서 원하는 슬라이드는 하지 못했지만, 다양한 UI를 개발해보았다.

스테이폴리오 사이트에서 여행지 숙소, 도시 선택, 인원, 가격범위, 스테이유형, 리셋버튼을 선택하여 구현해보았다. 그 중에서 공들인 부분은 인원, 가격범위, 스테이 유형 세 버튼임으로 이 세 부분을 중점으로 공유하고 싶은 코드, 숨기고 싶은 코드, 깨달은 점을 이야기해보고자 한다.

공유하고 싶은 코드

  function historyHandler() {
    const temptArr = [];
    let query = `category=`;
    names.forEach(name => {
      if (name === '펜션') {
        temptArr.push('pension');
      }
      if (name === '게스트하우스') {
        temptArr.push('guest');
      }
      if (name === '호텔') {
        temptArr.push('hotel');
      }
      if (name === '렌탈 하우스') {
        temptArr.push('rental');
      }
    });
    let subquery = '';
    temptArr.forEach(data => {
      subquery += `${data},`;
    });
    query += subquery;
    navigate(`/list?${query}`);
  }

카테고리 체크 된 것을 쿼리파라미터로 보내기 위해서 router url을 만들고, navigate로 url을 변경하는 코드이다. 깔끔하게 정리된 상태는 아니지만 공유하고 싶은 이유는, 이 코드를 위해서 이틀동안 엄청나게 검색을 했기 때문이다.
처음 이 코드를 쓸때만해도 url과 Uri의 차이, 쿼리파라미터 등 백엔드에게 정보를 전달하는 방법을 전혀 이해하지 못했다. 그렇기에 방법을 찾아보려고 쿼리파리미터에 대해 검색을 하고, 다른 분들께 질문하고 해서 끝내 해냈기에 기억에 남는다.

useEffect(() => {
    if (location.search === '') {
      fetch('http://localhost:8000/dormitories/', {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      })
        .then(res => res.json())
        .then(result => {
          setProducts(result.data);
        });
    }

    if (location.search !== '') {
      fetch(`http://localhost:8000/dormitories/search/${location.search}`, {
        method: 'GET',
      })
        .then(res => res.json())
        .then(result => {
          result.searchedDormitories && setProducts(result.searchedDormitories);
        });
    }
  }, [location]);

위와 이어서, 백과 통신하는 부분이다. use.location()을 사용하여서 uri에 쿼리파라미터 정보를 포함하여 보냈다. 화면에 성공적으로 원하는 카테고리만 뜰때 얼마나 기뻤는지 모른다. (Wework에서 백 담당분이랑 정말 기뻐했다...)

1️⃣ 숨기고 싶은 코드

  function closeModal(e) {
    const parentStyleVisible =
      e.target.parentElement.parentElement.style.visibility;
    let judge = false;
    if (parentStyleVisible === 'visible') {
      judge = true;
    }
    e.target.parentElement.parentElement.style.visibility = judge && 'hidden';
  }

각 작은 창에 달린 X버튼을 닫는 코드이다. 더 상위인 창을 닫기 위해서 무한으로 parentElement를 써서 올라갔다. 그리고 단축평가를 써보겠다고 Judge 라는 변수를 추가했다.

숨기고 싶은 이유

새로배운 것을 한번 쓰고 싶은 마음에 코드가 더욱 복잡해졌다. 더 간단한 방법으로 if문을 써도 되는 것을 알면서도 말이다. 앞으로도 새로운 것, 새롭게 알게된 것을 써보고 싶은 유혹 앞에 멈춰서 '새롭게 알게 된 것이 항상 좋은 것인가?' 고민을 해야할 것 같다.

리팩토링

가장 상위 page였던 list Page에 만든 창이 닫히는 setState를 prop으로 자식에게 보내었다. closModal() 함수 전체가 필요없어 삭제하고 버튼 onclick 이벤트에 setState를 이용해 visibility 값을 'hidden' 주었다.
├LayOutFirst.js

<BlackButton 
	content="적용하기"
    onClick={() => {
            onClickHandler();
            onHidden('hidden');
          	}}/>

├LayOutSecond.js

<BlackButton
	content="적용하기"
    onClick={e => {
          	putTitle(e);
          	onHidden('hidden');
        	}}/>

├LayOutThird.js

 <BlackButton
        content="적용하기"
        onClick={e => {
          checkedCatogories();
          changetitle();
          onHidden('hidden');
          historyHandler();
          resetcheckedCategoriesArr();
        }}/>

2️⃣ 숨기고 싶은 코드

따로 다뤄보고 싶어서 작성한 부분: 코드리뷰

6. 어떤 회사를 선택할까?

사람마다 회사를 보는 기준은 다를 수밖에 없다. 어떤 사람은 자신이 관심있는 서비스여야하고, 다른 사람은 이제 막 시작되어 급속으로 성장하는 곳을 선호할 수도 있다. 이 교육과정의 목표는 배움이기도 하지만 동시에 취업이기도 하다. 그렇기에 프로젝트를 하는 순간마다 '나는 어떤 곳에 취업해야하나(할 수 있을까/ 하고 싶을까)?'하는 고민이 들었다.

서비스 자체는 1순위에 두지 않는다. 그러나 선택할 수 있다면 책을 좋아하기에 전자책 서비스를 다루는 곳을 가고 싶다. 또는 3년정도 초중고 수학 학원강사 일을 해봐서 가르치는 일에 관심이 많아 교육관련 서비스도 좋겠다. 전공을 살려서 자기계발이나 상담 쪽 서비스도 좋다. 이런 분야라면 기본 지식과 경험이 있으니 서비스 전체를 객관적으로 바라보는 시각이 생길 것 같다.

1순위에 두는 것은 '내가 성장할 수 있는 곳인가?'이다. 내가 성장할 수 있는 곳이라하면 그것또한 조건이 다양할 수 있다. 내가 생각하는 이상적인 조건은 팀 분위기이다. 질문할 사람도 있고, 나에게 질문할 사람도 있는 분위기가 좋다. 데드라인이나 계획이 확고한 팀이면 더 좋겠다.

좋은 웹페이지 즐겨찾기