본문 바로가기
react

즐거운 넘블 프로젝트 후기

by HomieKim 2022. 12. 1.


넘블에서 진행한 커뮤니티 만들기 챌린지에 프론트엔드 개발자로 참여하였습니다!

프로젝트 회고록을 작성해 보려 합니다.

✔ 지원동기 

 모집 당시 면접, 코테 준비 때문에 굉장히 스트레스가 많았던 기억이 난다 ㅜ

서류 붙으면 코테 떨어지고, 코테 붙으면 면접 떨어지고,, 면접 한번 보러 서울 갔다 오면 몇 십만원 씩 깨지고,,

사실 프론트 엔드 개발이 하고 싶은건데 취업 준비 때문에 한동안 개발을 하지 못했음 

그냥 아무 생각없이 개발 하고 싶다는 생각이 있었는데 마침 프로젝트 모집 중이여서.. 면접도 떨어졌겠다.. 진짜 개발하고 싶어서 지원함

✔ 프로젝트 소개

 우리가 진행한 프로젝트 "소분 소분"은 내 아이디어가 채택 되어서 진행함 ㅎㅎ

동네 위치를 기반으로 같이 장보고 식료품을 나눌 수 있는 서비스!

나는 요리를 자주 하지 않지만 유튜브에서 레시피 따라하면서 요리하는 걸 재밌어함

문제는 요리를 자주 하는게 아니니 한 번 요리 하면 남는 야채나 재료들이 많아서 결국 버린 기억이 많다.

애초에 양파 하나 대파 한 개 이렇게 사고 싶은데 그렇게는 팔지 않거나 팔아도 몇 배로 비싸서 이런 서비스가 있으면 좋겠다고 생각한 적이 있음

✔  주요 개발 내용

🖥️ 동네 인증 페이지 개발 

  • 카카오 로컬 api를 연동하여 동네를 검색하고 geolocation api를 사용하여 반경 6km 이내의 사용자에게는 경고 모달을 표시하도록 개발!
  • 여기서 좀 신경 썼던 부분이 geoLocation api를 사용해서 내 현재 위치의 위경도 값을 가져오는데 이게 나중에 마트를 선택하는 페이지에서도 내 위치 값이 필요하고 성공 시, 에러 났을 시에 콜백함수를 따로 달아 줘야 되기 때문에 코드가 엄청 길어 졌음
  • 재사용성과 코드 가독성을 위해 useGeoLocation 이라는 custum hook을 따로 만들어서 사용 이번 프로젝트에서 전체적으로 custum hook을 최대한 활용하려고 노력함

내 위치를 가져오는 custum hook

const UseGeoLocation = () => {
  const [location, setLocation] = useState<LocationType>({
    loaded: false,
    coords: { latitude: 0, longitude: 0 },
  })

  const onSuccess = (resLocation: SuccessInfoType) => {
    setLocation({
      loaded: true,
      coords: {
        latitude: resLocation.coords.latitude,
        longitude: resLocation.coords.longitude,
      },
    })
  }

  const onError = (error: ErrorType) => {
    setLocation({
      loaded: true,
      error,
    })
  }

  useEffect(() => {
    // navigator 객체 안에 geolocation이 없다면
    // 위치 정보가 없는 것.
    if (!('geolocation' in navigator)) {
      onError({
        code: 0,
        message: 'Geolocation not supported',
      })
    }
    navigator.geolocation.getCurrentPosition(onSuccess, onError)
  }, [])

  return { loading: location.loaded, lng: location.coords?.longitude, lat: location.coords?.latitude }
}

🖥️ 메인 페이지 개발

  • 재사용 가능한 Card 컴포넌트 개발, 해당 card 컴포넌트가 디자인이 조금씩 바뀌면서 재활용이 되어야 했기 때문에 props로 isCompelete, isVertical 이런 불린 값으로 구분하여 css를 사용함
  • 스타일링 할 때 scss를 사용하고 조건 부로 className을 적용해야할 때는 classnames 모듈을 썼는데 솔직히 좀 불편했을 쓰면서 CSS-in-JS가 이래서 편하구나 싶었는데 이건 뒤에 회고에 작성하겠음
  • infinite scroll 방식으로 데이터를 보여주는데 몇번 해본적은 있는데 마지막 데이터인지, 유저 스크롤이 마지막에 있는지 등등 side effect 가 있는 로직이기 때문에 custum hook 으로 깔끔하게 코드를 작성함

infinite scroll data를 로드 하는 custum hook

import { useEffect } from 'react'
import { getFeedAPI } from 'apis/feed'
import { useInfiniteQuery } from 'react-query'
import { category, FeedResponse } from 'types'

interface Props {
  inView: boolean
  selectedCategory: category
}

const useFeed = ({ inView, selectedCategory }: Props) => {
  const size = window.innerWidth > 780 ? 12 : 5
  const { data, fetchNextPage, isLoading } = useInfiniteQuery<FeedResponse>(
    ['feedList', selectedCategory],
    ({ pageParam = 0 }) => getFeedAPI({ pageParam, category: selectedCategory, size }),
    {
      getNextPageParam: (lastPage) => {
        if (!lastPage.isLast) return lastPage.nowPage + 1
        return undefined
      },
      staleTime: Infinity,
      refetchOnMount: true,
    }
  )

  const feedList = data?.pages
    .flat()
    .map((v) => v.feedList)
    .flat()
  const isEmpty = data?.pages[0]?.feedList.length === 0
  const isReachingEnd = isEmpty || (data && data.pages[data.pages.length - 1]?.isLast === true)
  const hasMorePosts = !isEmpty && !isReachingEnd
  const readToLoad = hasMorePosts && !isLoading

  useEffect(() => {
    if (inView && readToLoad) {
      fetchNextPage()
    }
  }, [inView, readToLoad, fetchNextPage])

  return { feedList, isLoading, readToLoad }
}

export default useFeed

🖥️ 좋아요 / 참여하기 기능 구현

  • 좋아요 / 참여하기의 경우 버튼을 클릭할 시 toggle 방식으로 색깔이 바뀌고 서버에 좋아요 요청 / 좋아요 취소 요청을 을 보내 주어야 합니다.
  • 데이터 패칭 시 로딩이나 새로고침 없이 개선된 ui를 유저에게 제공하기위해 react-query 기반에 optimistic ui를 사용하였습니다.
import { postLikeAPI } from 'apis/feed'
import { useMutation, useQueryClient } from 'react-query'
import { detailData } from 'types'
import { AxiosError } from 'axios'

const useLike = () => {
  const queryClient = useQueryClient()
  return useMutation(postLikeAPI, {
    onMutate: async ({ postId }) => {
      await queryClient.cancelQueries(['getDetailAPI', postId]) // 쿼리 취소
      const prevUserData = queryClient.getQueryData<detailData>(['getDetailAPI', postId])
      if (prevUserData) {
        const newUserDetail = prevUserData.isLike
          ? { ...prevUserData, isLike: false }
          : { ...prevUserData, isLike: true }
        queryClient.setQueryData<detailData>(['getDetailAPI', postId], newUserDetail)
      }
      return { prevUserData }
    },
    onError: (err: AxiosError, { postId }, context?: { prevUserData: detailData | undefined }) => {
      if (context?.prevUserData) {
        queryClient.setQueryData<detailData>(['getDetailAPI', postId], context.prevUserData)
      }
    },
    onSettled: (data, err, { postId }) => {
      queryClient.invalidateQueries({
        queryKey: ['getDetailAPI', postId],
        refetchActive: false,
      })
      queryClient.invalidateQueries({
        queryKey: ['LikeList'],
        refetchInactive: true,
      })
    },
  })
}

export default useLike

 

이 외에도

  • 개발 환경 구축 : S3 + Cloud Front 배포 후 Route 53을 이용한 도메인 주소 사용, github action을 사용하여 배포 자동화 구축
  • 카카오 맵 초기 연동
  • meta tag 작성
  • 각종 에러 트러블 슈팅 : 로그아웃 시 token 갱신, 쿼리키 초기화시 효율적인 데이터 리패치 등 논리적인 에러를 많이 해결하였습니다. 

등등 전반적인 프론트엔드 개발에 모두 참여하였습니다.

 

✔ 회고

최근 진행한 프로젝트 들은 전부 내가 개발 환경을 셋팅했는데 이번엔 다른 팀원분이 해주셔서 처음에 적응하기가 좀 어려웠음 내 코드에 갖힌다는게 이런거구나 싶더라.. 그래도 레포 카피해서 몇번 붙여 보니 금방 적응 함
오랜 만에 개발하니까 재밌었음 그리고 팀원들이 전부 책임감 넘치고 적극적이여서 나랑 넘 잘맞았음 가끔 보면 조금 어렵다 싶으면 어느 정도만 구현하고 대충 마무리 하기로 한 경험도 있는데 나 빼고 프론트 엔드 팀원 분들이 현직 퍼블리셔 출신 개발자님도 계시고.. 다들 문제 해결에 적극적으로 참여해주셔서 재밌었음
이번 프로젝트에서 제일 얻어 가는게 react-query에 대한 이해도를 많이 올릴 수 있었음 react-query 이런 작은 프로젝트에 3번째 써보는데 그냥 가져오는게 아니라 상황에 따른 staleTime이나 refetch 타이밍 등을 최대한 신경썼고 refetchActive, refetchPage 등등 써본적 없던 쿼리 옵션을 많이 써봤음
 기억 남는 게 bakcground로 fetching 할때 마운트시 데이터를 undefined로 찾길래 isFetching에 로딩 컴포넌트를 걸어줬는데 개발하다 보니 갑자기 optimistic ui 적용한 page가 깜빡거리는길래 왜그런지 찾는걸 엄청 해멧던 기억이 난다...
 반응형 웹 살짝 맛만 본 정도로 구현하긴 했지만, 묘하게 비효율적인 느낌을 받아음, px 값을 pc기준으로 한 번, 모바일 화면 기준일 때 이 px를 vw에 맞춰서 변환해주는 함수 한 번 총 두번 써야함
 scss 에 mix-in 함수를 팀원이 다 만들어 주셔서 그냥 입력만 하면 되긴 했는데 하면서 더 좋은 방법없을까 고민했었음, 찾아보니까 css-in-js 써도 theme provider로 color 같은거 미리 정의하듯이 함수도 넣을 수 있는 것 같음 사실 별차이 없다 느꼈는데 써보면 써볼 수록 css-in-js가 개발하기는 더 편한 것 같음.
 사실 상 css-in-js 써도 scss 방식으로 쓰는데 theme provider에 mix-in 함수 같은거 만들어서 가져다 쓰는게 개인적으로 는 더 편할 것 같음, 안 익숙해서 그렇겠지만 mix-in파일을 @use 이렇게 가져오고 @include 이렇게 함수 쓰는게 좀 불편했음

😀 좋았던 점

- 오랜만에 다른 사람이랑 같이 개발해서 새로운 것들 배울 수 있었음 반응형 css 를 적용할 때 scss mix in 으로 px 값에 따라 vw 값으로 변환 해주는 함수를 팀원 분이 만들어 주셨는데, 반응형 적용할 때 미디어 쿼리 하나하나 달아줬는데 저렇게 하니까 신기하더라 (mix-in 쓰는 사람 첨봄.. 보니까 100 * px / 모바일 기준 사이즈 이런 느낌이더라)

 

- 직전에 프로젝트 할 때 코드가 너무 길어지고 가독성이 너무 안좋은 문제가 있었는데 이번에 custum hook을 최대한 활용해서 깔끔하게 코드를 쓰려고 노력했음, 뭔가 개선된 시도를 할 수 있었던 점이 좋았다.

 

- 에러 잡는게 조금 능숙해진 것 같음. 팀원 분들이 에러가 난다고 카톡에 공유해 준 경우가 있었는데 서버에서 나오는 에러 제외하고는 대부분 내가 경험 해봤던 거더라. token이 갱신이 안된다던지.. 쿼리키가 초기화 안됐다던지 언마운트 시에 recoil을 초기화 안해줬다던지.. 팀에 도움이 된거 같아서 좋았고 뭔가 성장한 기분이었음

 

- aws 환경에 어느정도 적응한 것? vercel, netlify 이런 호스팅 서비스만 쓰다가 최근 부터docker, aws 써보고 있는데 너무 어려움.. 근데 리액트에서 S3 올리는건 레퍼런스 보고 따라하니까 크게 어려움 없었음, 오히려 재밌었다 github action 연동하는 것도 딱히 에러나지도 않고 서비스가 ec2 쓰다가 s3 + cloude front 써보니까 오히려 직관적이다 생각했음 이 프로젝트랑 졸작을 같이 했는데 졸작 인프라는 ec2 + docker image build + github action인데 ssh 접속이 안되서 만지다가 ec2 통으로 날려먹어서 재배포 해야되는 상황이다 지금... ㅋㅋ docker 이미지도 git action으로 자동 배포할라니까 action runner 연동하고.. ssh 접속해서 도커설치하고.. 다시 공부해야 겠따...

 

😂 아쉬웠던 점

- 물론 엄청난 클린코드를 작성하는 건 아니지만.. 이제 디자이너가 준 디자인을 구현하고 api 문서 보고 연동하고 이런건 어느정도 능숙하게 하는 것 같다. 다만 이번 프로젝트에서 기술적으로 새로운 시도를 많이 해 볼 수가 없었음 ㅜ

 

- 프로젝트 모집할 때 주제가 위치기반 커뮤티니 만들기 여서 재밌겠다 생각했는데 geolocationAPI나 카카오 맵, 로컬 API 이런건 전부 처음 써보긴 했는데 새로운 지식을 배우는 느낌 보다는 레퍼런스도 굉장히 많고 그냥 문서보면서 가져다 적용하는 수준에서 해결할 수 있는 수준의 구현이였다. (근데 geolocationAPI가 https만 지원하는건 마감 이틀전에 알았음..ㅋㅋ 벡엔드에서 https 당장 적용하는 게 어렵다고 하셔서 관련 로직을 배포할 때 제외했다ㅠ)

 

- 챌린지 형식의 프로젝트라 역시 급하게 진행되는게 여전히 아쉬움 코드리뷰도 하고 싶고 지금 폴더구조도 조금 고치고 싶은게 있긴함 근데 시간도 없고 같이하는 프로젝트다 보니 이런 부분은 그냥 넘어간 점이 아쉽다

 

- 여전히 내 코드에 갇히는 느낌 사실 이런 포폴성? (이걸로 창업하거나 돈버는게 아니니..) 프로젝트를 한 3 번 정도 참여한 것 같은데 나보다 월등히 경험 많은 개발자분을 만나기가 어려움.. 물론 할 때마다 재밌고 배워가는게 많지만 뭔가 번뜩이는 인사이트를 얻기는 힘든 게 사실이다.

 취업하고 싶은 이유도 당연히 돈 벌고 싶어서도 있지만 혼자 공부하고 프로젝트 찾아 다녀도 실제 경험 많은 개발자 분과 같이 기술 토론하고 리뷰받고 성장하고 나의 부족한점을 발견하는? 그런 경험 하기는 힘든 것 같다. 이런 이유로 열심히 취준하고 있지만 쉽지 않다 ㅜ


 최근에 한 프로젝트 중에 제일 재밌게 한 것같고 개발하는 데 즐거움을 느낀 것 같아 너무 좋았던 "소분 소분" 프로젝트 후기였습니다.

이제 또 코테랑 면접준비에 집중해야 하는 시기가 될 것 같은데 이 경험이 많은 도움이 되었으면 좋겠다!!

 

댓글