DEVLOG|개발 블로그

신입 개발자 포트폴리오용 협업 프로젝트 회고록

March 7, 2021 6:55 PM:react

시작하기에 앞서

당시 프로젝트를 진행하였을 때에는 아무런 협업 경험이 없던 취준생 시절이였습니다. 이 내용이 취준생 시절 협업 경험을 늘리고 싶고, 취업용 포트폴리오에 넣을만한 프로젝트를 개발하는게 목적이였기 때문에 동일한 목적을 가진 취준생분들께는 작은 도움이 될 수 있겠지만 이미 개발자로 일하고 계신 분들께는 내용이 지루하거나 귀여워 보일 수 있습니다.

커뮤니티와 프로젝트 시작

okky라는 개발자 커뮤니티가 있다. 상당히 역사가 오래되기도 했고, 많은 개발자분들이 이용하는 커뮤니티다. 항상 여기서 정보도 얻고, 질문 글 중 내가 경험해봤던 문제면 답변을 달아 활동하기도 했다. 취준생이였던 나는 자주 정기모임/스터디 카테고리를 눈팅했다. 나와 동일한 목적을 가진 분이 스터디를 구한다는 글을 볼 수 있었다.

메일을 보내고 프론트엔드 개발자는 나 포함해서 2명, 백엔드 개발자분이 두분 이렇게 총 4명으로 팀이 구성되었다. 취준생분들도 계셨고 학생이신 분도 있었다. 단톡방에 초대 후 이런저런 얘기를 나누는 도중 팀원 중 한분한테 개인 톡이 왔다. 같이 프론트엔드 개발을 하게 된 사람인데, 갑자기 잘하는 사람인 것 같다고 칭찬을 한다. 뭐지 이 사람.. 카카오톡 프로필 상태명에 블로그 주소가 적혀있었다. 그걸 보고서 잘하는 사람이라고 착각한 것 같다. 그 뒤 "아, 네. 잘해봅시다!"라는 말을 하고 몇 시간 후에 상태명을 지웠다.

단톡방에서 인사를 하고 이런 저런 얘기를 하다가 슬랙이라는 메신저를 이용해서 내용을 주고받기로 결정했다. 비트코인 거래소라는 주제를 가지고 개발은 시작됬다. 다들 특정한 주제를 생각해보진 않아서, 한 분이 의견을 낸 것 중에 진행을 하기로 했다. 비트코인은 살면서 관심도 없었고 하물며 주식도 잘 모른다. 비트코인 거래소를 개발하기 위해서는 UI/UX 측면에서 '비트코인'이라는 개념을 알고있어야 좀 더 깔끔하고 완성도 높은 개발을 할 수 있을 것 같았다. 그래서 비트코인에 대해 좀 공부해보고, 리액트를 이용해서 빗썸에서 제공하는 무료 오픈 API를 이용해 개인적으로 데이터 시각화 프로젝트를 한 개 시작했다.

주제가 비트코인 거래소로 지정되고 얼마 지나지 않아 비트코인 거래소 의견을 낸 사람이 탈주를 했다. 앞서 개인 톡이 왔던 그 사람이 서로 잘해보자고 해놓고 오랜 시간이 지나지 않아 탈주해버린 것이다. 나를 포함해서 팀원들을 적지 않게 멘붕이왔다.

당시 포트폴리오가 거의 완성 상태였던 나는, 이번에 하는 프로젝트를 얼른 완성 시켜서 한 개라도 더 넣고 빨리 취업하려는 생각이였다. 한 사람을 더 구하자는 의견이 있었다. 나와 같이 개발할 사람이니 나보고 결정하라고 하셨다. 그 때에는 혼자할 수 있을 것 같았다. 그리고 새로운 사람이 와서 그 사람이 또 탈주를 해버리면 어떡하지라는 생각에 그냥 혼자하겠다고 했다. 지금 생각해보면 조금 후회된다.

첫 협업과 프로젝트 구성

한 사람이 탈주하고 다시 정해진 주제는 여행을 주제로 한 어플리케이션. 참고한 사이트는 트립어드바이저에어비앤비 두 개를 합쳐 우리 팀 이름은 트립앤비엔비로 정해졌다(...)

다들 협업 경험이 없던 터라 처음에 어떻게 시작해야 할지 감이 오지 않았다. 일단 나는 내가 해야할 일을 정하고 하나씩 하기로 했다. 혼자 하기에 UI에 필요한 모든 컴포넌트를 직접 개발할 수 없을 것 같아 라이브러리를 사용하기로 했다. Vue는 줄곧 사용해왔지만 매번 Vue만을 사용할 수 없으니 리액트를 해보기로 했다. 어차피 싱글 페이지 어플리케이션이라는 개념만 이해한다면 어떤 프레임워크/라이브러리를 이용하던 큰 어려움이 없었을 거라 생각했기 때문이다.

상태 관리 패턴은 각 프레임워크/라이브러리마다 지원하는 에코 시스템에 따라 다르므로 리액트에서 상태 관리 패턴 라이브러리가 어떤게 있는지 살펴보기로 했다. 제일 인기가 있는 리덕스와 Vue에서 사용하던 Vuex와 비슷한 Mobx가 있었다. 리덕스는 Vuex에서 사용하던 패턴과 전혀 다른 패턴이라고 느꼈고 어려웠다. 개념을 이해하기 어려워 리덕스를 사용해서 프로젝트를 구성한다고 해도 나중에 문제가 일어났을 때 해결하는데 애를 먹을 것 같았다. 그래서 조금 더 쉬운 상태 관리를 하기 위해 리액트에서 제공하는 Context API를 이용해서 상태 관리를 하기로 했다.

큰 틀

큰 주제에 대해서 의논해보기로 했다. 제일 먼저 사용할 기술 스택, 백엔드는 장고를 사용하기로 했고 프론트는 리액트로 구성했다. 인증/지도에 대해서 생각해보았는데, 모두 프론트에서 핸들링할 수 있을 문제인 것 같아 지도는 카카오 지도 API를 활용했고, 인증도 카카오 로그인을 이용해 소셜 로그인으로 구성했다.

지금와서 드는 생각이지만 나 혼자 너무 욕심을 부린 것 같다. 나 혼자 하는 프로젝트도 아니고 인증 같은 경우엔 따로 자체적으로 구축해서 인증 서비스를 구축하는 것 자체로 백엔드와 프론트엔드 모두 공부할 수 있는 기회가 될 수 있었을 것 같은데, 빨리 하고자 하는 마음에 외부 인증 서비스를 사용하여 공부할 수 있는 기회를 놓친것 같다고 생각했다.

하지만 지도같은 경우엔 어떤 내부 데이터를 가지고 있는 상태가 아니였기 때문에 외부 라이브러리를 사용했다.

무엇을 협업해야 하는가

인증과 관광 명소, 음식점, 숙박 시설에 대해서 보여주는 서비스도 모두 외부 라이브러리를 사용해서 프론트엔드에서 전부 핸들링할 수 있었다. 그러므로 백엔드에서 해야할 일이 상대적으로 적었다. 초기에는 이게 과연 협업이 맞는가 싶었다. 좋지 않은 흐름인 것 같았다.

하지만, 점점 더 해야할 일이 생겼고 자체 API가 필요한 경우 백엔드 개발자분들께 요청하면서 진행한 부분이 있다. 가령 지역을 검색한다고 가정하고, 그 지역의 관광 명소/음식점/숙박 시설에 대한 리뷰 데이터, 사진 등이 필요했다. 이 부분에 대해서 백엔드 개발자분들과 어떻게 진행하면 될지 의논하고 서로 조율하는 과정에서 협업이라는 것이 이런거구나 하고 느꼈다.

리액트와 타입스크립트

평소 타입스크립트에 대한 공부 열망이 엄청 있었다. 하지만 뷰에서는 특히, 버전 2에서는 타입스크립트 지원이 엉망이였다. vscode 확장 프로그램을 통해 어느정도 불편함을 해소할 수 있었지만 많은 부분들이 지원이 안되는게 항상 불편했었다. 하지만 리액트는 타입스크립트와 호환성이 잘 맞고 타입스크립트를 사용하면서 리액트를 사용한다면 리액트도 배울 수 있고 타입스크립트도 공부할 수 있을 것 같아 해당 스택을 사용했다.

const Container: React.FC = () => {
  const [marker, setMarker] = useState(null)
  const [markerPlace, setMarkerPlace] = useState(null)
  const [visibleDetail, setVisibleDetail] = useState(false)
  const [detailItem, setDetailItem] = useState(null)
  const [displayMarkers, setDisplayMarkers] = useState([])

  const onChangeMarker = useCallback(() => {
    return marker || null
  }, [marker])

  const getVisible = useCallback(() => visibleDetail || false, [visibleDetail])

  return (
    <StyledMap id="map">
      <Marker.Provider
        value={{
          marker,
          markerPlace,
          visibleDetail,
          detailItem,
          displayMarkers,
          setMarker,
          setMarkerPlace,
          setVisibleDetail,
          setDetailItem,
          setDisplayMarkers
        }}
      >
        <SearchBox />
        <SearchButton />
        <Filter />
        <MapCenter />
        {onChangeMarker() && <OverlayContainer />}
        {getVisible() && (
          <>
            <DetailContainer />
            <TileLoadedEvent />
          </>
        )}
      </Marker.Provider>
    </StyledMap>
  )
}

맵 컴포넌트 중 일부인데, useState 혹은 useCallback, useMemo 등을 이용해서 렌더링 최적화를 할 수 있다는 점이 매력적으로 느껴졌다. 그리고 데이터를 구성하기 위한 훅을 컴포넌트 파일 외부로 빼서 따로 커스텀 훅을 구성할 수 있다는 점도 매우 우아하게 느껴졌다. 로직이 분리되어 있으니 코드를 좀 더 깔끔하게 작성할 수 있을 것 같다는 생각이 들었다.

Context API를 이용해서 내부 컴포넌트 깊이를 생각할 필요 없이 부모 컴포넌트의 데이터를 뿌려줄 수 있는 점과 그 데이터의 타입이 자동완성될 수 있다는 점이 뷰보다는 리액트가 좀 더 나은 부분이라고 생각했다. 뷰에서도 provideinject를 이용해서 내부 컴포넌트에 데이터를 뿌려줄 수 있다.

UI/UX

큰 틀은 직방을 참고했다. 지역을 검색하면 해당 지역의 음식점/관광 명소/숙박 시설이 마커로 찍히며 그 마커를 클릭하면 직방처럼 오른쪽에 그 지역의 자세한 정보를 띄우는 UI로 구성했다.

API에서 데이터를 가져오는 부분은 모두 로딩중인 상태를 유저에게 보여주기 위해 많은 방법이 있는데, 이번 프로젝트에서는 스켈레톤 로딩을 채택했다.

skeleton loading in youtube

좋은 UI/UX 개념이라고 생각되서 앞으로 모든 프로젝트에 이런 부분을 채택하려고 한다.

skeleton loading in tripnbnb

모든 부분에 이런 스켈레톤 로딩 UI를 구성하여 좀 더 깔끔하게 보이기 위해 노력했다.

지역 이미지 가져오기

지역의 이미지를 가져오기 위해서 다른 외부 API를 연결하기로 했다. 원래 계획은 인스타그램에서 지역의 키워드로 해당 키워드를 가진 인스타그램 태그 게시글에서 이미지를 긁어와 노출하려고 했지만 인스타그램 API가 다소 어려운 부분이 있어 이 부분은 협의했던 대로 진행되지 않았다. 대신 네이버 검색 API에서 키워드로 이미지를 가져오는 걸로 대체했다.

image-tripnbnb

다만 장소를 특정하면 이미지를 가져오는게 어려워진다. 예를 들어 '리츠호텔'이라는 호텔은 전국에 해당 리츠호텔을 제외하고 더 존재할 수 있으며 그 수가 2개, 3개 혹은 셀 수 없을 만큼 있을 수도 있다. 따라서 '서울 명동 리츠호텔' 이런식으로 검색하면 좀 더 자세한 이미지를 얻을 수 있으나 이미지를 얻지 못하는 경우도 있을 수 있기 때문에 이런 상황에서는 따로 지역을 붙이지 않고 이미지를 검색하도록 했다.

다른 상황도 있었다. 관광 명소나 음식점 혹은 숙박 시설이 사람 이름인 경우가 있다. 음식점에 주로 많이 존재한다. 사람 이름을 네이버에 검색하면 높은 확률로 그 사람에 대한 이미지가 나온다. 예를 들어 음식점 이름이 '박지성 감자탕' 이라면 실제로 존재하는 '박지성 감자탕'에 대한 음식점의 이미지가 반환될까 아니면 '박지성'과 '감자탕'의 이미지가 반환될까? 이 부분은 팀 내에서도 이슈로 다루긴 했지만 외부 API를 사용하고 있어서 직접적으로 핸들링하기가 어렵다.

AI를 사용하는 것도 아니기 때문에 해당 이미지가 박지성인지 감자탕인지 박지성 감자탕이라는 음식점인지 판단할 수 없다.

숙박 시설 예약

숙박 시설은 예약할 수 있어야 한다. 팀 내에서 결정된 기능이다.

image-tripnbnb-reservation

예약하기 버튼을 누르면 해당 숙박 시설에 대해서 예약할 수 있는 팝업이 뜬다. 실제 숙박 시설의 데이터를 가지고 있는게 아니기 때문에 예약이라는 기능을 흉내내기만 하였다. 숙박 시설이 예약 관련 데이터에 대한 전산 시스템이 있다면 숙박 시설에서 API를 연결해 예약 데이터를 핸들링할 수 있을 것 같다.

예약 기능에 대해 진행할 때에 제일 어려웠던 점은 서로 다른 방 타입과 날짜에 대해 예약이 가능한지 불가능한지에 대한 여부였다. 예약이라는 기능을 만들기로 하고 전제로 두던 조건이 방 타입은 모든 숙박 시설이 5개를 가지며 각 타입의 방은 모두 한 개씩 있다고 가정하고 만들었다. 백엔드에서 이런 부분에 대해 핸들링하는데, 어떤식으로 조율해야 할지 애매해서 시간이 많이 들었던 것 같다.

뭔가 정확하게 체계를 잡고 이 기능에 대해 진행했다면 좀 더 괜찮은 기능이 되지 않았을까 하는 생각이 든다.

마무리

프로젝트를 진행하면서 팀원들에게 이 프로젝트의 난이도에 대해서 질문했던 적이 있다. 쉽지 않았다고 팀원들은 이야기했다. 내가 느끼기에 진행했던 프로젝트에서 프론트엔드가 나 혼자가 아닌 한명이라도 더 있었다면 난이도가 매우 쉽다고 느껴졌을 것이다.

'맵'이라는 한정된 자원에서 무엇을 어떻게 UI를 구성해야 할지에 대해서 시간을 제일 많이 소비했다고 생각한다. 완성도 부분에서 생각한다면 해당 프로젝트는 그리 높지 않다고 스스로 평가할 수 있다. 왜냐하면 초기에 많은 것을 기획하고 의논해서 시작한 프로젝트가 아니기 때문에 부족한 부분이 많다. 허술한 부분도 많고, 외부 라이브러리에 의존한게 컸기 때문에 내부 프로젝트에 대한 완성도는 낮은 편이라고 생각한다.

배운 점은 많다고 생각한다. 사실 웹 개발에서 서버와 클라이언트의 사이를 생각해보면 데이터를 주고 받는게 모든 것이라고 생각한다. 데이터를 어떻게 주고 어떻게 받느냐인데, 이런 부분에선 많은 것을 배운 것 같다. 부족한 것도 있었다. 다음 프로젝트에서는 좀 더 구체화해서 데이터를 주고 받을 필요가 있다.

이런 사이드 프로젝트를 진행하며 배우는 점은 무궁무진하며 내가 혼자 진행하는 프로젝트와는 또 다른 지식을 배울 수 있다. 만약 다른 사람과 사이드 프로젝트를 진행하는 걸 고민하는 취준생이 있다면 주저없이 진행해볼 것을 권한다. 어떤 목적으로든 다양한 사람들과 다양한 문제에 대해서 해결하는 것이 재밌기도 하고 좋은 경험이 될 수도 있다.