병아리 개발자의 첫 웹뷰 개발기: 파일럿 프로젝트

Mar.16.2023 전소이

Web Frontend

안녕하세요! 지난 1월 3일 푸드서비스개발팀에 웹 프론트엔드 신입 개발자로 합류한 전소이입니다.
입사가 정말 실감 나지 않았는데, 입사한 지도 벌써 두 달이 지나가고 있네요. 🥹

오늘은 입사 첫 달 동안 진행한 파일럿 프로젝트 후기를 공유하고자 합니다.
좋은 파일럿 프로젝트 경험을 위해, 그리고 신입 팀원으로 잘 적응하기 위해 제가 노력했던 부분들과 느낀 점을 담아보려 하는데요. 곧 파일럿 프로젝트를 진행하는 분들께 조금이나마 도움이 될 수 있지 않을까 기대해봅니다.
또 배민푸드서비스개발팀 웹프론트 파트의 분위기도 살짝 엿보고 가실 수 있을 것 같습니다! 😎


좋은 파일럿 프로젝트를 향해

파일럿 프로젝트의 목표에 집중하기

파일럿 프로젝트는 실무에 쉽고 빠르게 적응하기 위한 준비 단계입니다. 모든 프로젝트가 그렇듯 목표를 잘 이해하고 수행해야 프로젝트를 진행하는 의미가 있다고 생각했어요.

이번 프로젝트의 주요 목표를 요약하면 다음과 같습니다.

  • 배달의 민족 앱 내의 웹뷰 개발 과정을 경험한다.
  • 기획서와 API 문서, 피그마를 접한다.
  • 서비스되는 지면의 플로우를 이해한다.
  • AWS 서비스를 사용해보고 배포 파이프라인 흐름을 파악한다.

제가 개발한 지면은 배달의 민족 앱에서 ‘My배민’을 선택하고 등급별 혜택을 선택하면 확인할 수 있는 등급별 혜택 안내 지면인데요. 바로 아래 이미지의 지면입니다.

‘목표를 잘 달성하면 꽤 괜찮은 파일럿 프로젝트다!’라고 생각하며, 위 목표를 항상 염두에 두고 작업하려 했습니다.

요구사항 파악하기

프로젝트를 본격적으로 시작하기 전 첫 주에는 웹뷰 개발을 위한 준비와 요구사항 점검에 집중했습니다. 도메인 용어가 낯설어 요구사항을 제대로 이해했는지 재확인하고 정리하는 기간을 가졌어요.

헷갈리는 부분은 온보딩을 도와주시는 돌보미님께 여쭙기도 하고 🥹

각 앱 스킴과 API가 어디서 사용될지 확인하며 프로젝트 저장소의 README.md에 요구사항을 정리했습니다.

웹뷰 개발 적응하기

요구사항 파악 외에도 웹뷰와 관련된 인터페이스를 알아보는 시간을 가졌습니다. 웹뷰 개발이 처음이다 보니 초반에는 조금 어색하기도 했는데요. 😵‍💫
시뮬레이터 등 개발 환경을 먼저 세팅하고, 웹뷰 개발에 필요한 인터페이스를 위키에서 찾아보고 팀원분들께 물어보며 테스트한 후에 본격적으로 개발을 시작했습니다.

이후 리뷰 단계에서 놓친 부분들을 다시 챙길 수 있었어요. 리뷰 중 크게 두 가지가 기억에 남는데요.

첫 번째는 ‘웹뷰 개발 시 viewport 메타 태그에 어떤 값이 필요한지와 safe-area 알아보기’입니다.

다양한 모바일 기기에서 콘텐츠가 잘 보이도록, viewport 메타 태그와 safe-area를 적용하는 일이 필요했습니다. 특히 viewport 메타 태그 content 값 중에서는 user-scalable과 viewport-fit을 새롭게 알게 되었습니다.

user-scalable은 사용자가 화면을 확대, 축소할 수 있도록 하는 설정입니다. 해당 값을 알아보면서, 이미지 해상도나 Input 요소에 포커싱될 때 확대될 수 있는 이슈 등으로 실제 서비스에서는 스케일 조정을 막아두고 있다는 점과 웹 접근성 가이드라인에서는 최소 2배 확대까지 가능하도록 설정하기를 권장하고 있다는 점을 알게 되었습니다.


출처: W3C – CSS Round Display Level 1, WebKit Blog – Designing Websites for iPhone X

viewport-fit은 디스플레이 영역의 모양이 다양해지면서 나온 설정입니다. 예를 들어 iPhone X처럼 디스플레이 영역이 굴곡지거나, 워치와 같이 동그란 형태의 디스플레이가 있는데요. 네모난 콘텐츠와 디스플레이의 모양이 다른 상황이 생기게 됩니다. 위의 이미지처럼요.

이때 viewport-fit 설정을 통해 콘텐츠가 화면을 좀 넘어가더라도 꽉 채울지, 여백이 생기더라도 콘텐츠를 온전히 보여줄지 선택할 수 있습니다.

여기에 네비게이션, 탭 바 등에 의해 가려지지 않는 영역인 safe-area까지 적용하여 콘텐츠가 화면에 잘 나타나도록 설정해주었습니다.

두 번째는 ‘토스트 UI는 네이티브 앱이 제공하는 걸 사용하기보다, 웹뷰에서 직접 개발하기’입니다.

에러 메시지를 사용자에게 제공하기 위해 토스트 UI가 필요했고, 처음에는 앱이 제공하는 인터페이스를 이용하여 앱의 토스트 UI를 사용했습니다. 이때 받았던 리뷰가, 보통 앱이 제공하는 UI를 사용하기보다 웹으로 직접 구현한다는 내용이었어요. 이유는 다음과 같습니다.

  1. 앱의 인터페이스를 사용하면, 기기와 버전마다 동작이 달라질 수 있어 그에 대해 처리할 일이 늘어날 수 있다.
  2. 스크린리더가 웹뷰의 요소를 먼저 읽을지, 앱에서 제공하는 UI를 먼저 읽을지 그 우선순위를 파악하기 어렵다.
  3. 이슈에 빠르게 대응할 수 있다는 웹뷰의 장점을 더 잘 사용할 수 있다.

위와 같은 부분들은 생각 못 하고 있었는데, 앞으로 작업하면서 어떤 부분들을 고려해야 할지 알 수 있었어요.

기술 스택 선정하기

배민푸드서비스개발팀 웹프론트 파트에서는 리액트와 타입스크립트를 기본으로 하고, 다른 기술 스택은 상황에 따라 선택하고 있습니다. 이번 프로젝트 요구사항에서도 CSS와 상태 관리 라이브러리는 자유롭게 선택하고 그 이유를 설명해달라는 항목이 있었어요.

저는 스타일링에는 SCSS를 데이터 페칭에는 react-query를 선택했습니다.

SCSS를 선택한 이유는 크게 두 가지였는데요.

  1. 지면 내의 상태 변화가 거의 없고, 따라서 스타일링이 동적으로 변화할 일도 거의 없다고 판단했고,

  2. 리액트를 쓰면서는 CSS-in-JS만 사용해왔는데, 이번에 SCSS를 사용하면서 CSS-in-JS와 CSS-in-CSS의 차이를 다시 한번 경험해보고 싶었기 때문입니다.

‘지금까지 써온 styled-component나 Emotion이 정말 더 편리한가? 그냥 익숙한 게 아닌가?’라는 생각에 다시 써보자는 생각으로 SCSS를 선택했어요. 실제로 사용해보니 내부에서 스타일링 변화가 많은 상황이 아니라면 SCSS로도 충분하다는 생각이 들었습니다. 오히려 어떤 태그로 구성했는지 한눈에 들어와서 시멘틱 태그와 접근성을 챙기기에 더 편할 수 있겠다고 느꼈어요.

전역 상태 관리에는 라이브러리를 따로 사용하지 않고, 리액트의 Context API를 사용했습니다. 기준은 ‘전역으로 관리할 상태가 얼마나 되는가?’였어요. 주어진 과제가 단일 지면이라 다른 지면을 넘나들며 공유할 상태가 없었고요. 단일 지면 내에서도 전역으로 공유할 상태가 많지 않은 것으로 파악했습니다. 지면에 접근할 때 앱에서 가져오는 사용자 정보와, 모달, 토스트 등을 위한 UI 상태뿐이었어요. 컨텍스트가 다루는 상태가 변할 일이 거의 없기 때문에 Context API를 사용해도 괜찮겠다고 생각했습니다.

대신 데이터 페칭에는 react-query를 사용했습니다. react-query가 제공하는 API 요청 상태와 ErrorBoundary, onError 등의 옵션이 개발할 때 굉장히 유용했고, 훨씬 깔끔하고 직관적인 코드를 만들어준 경험이 좋았습니다. 데모를 진행하면서 ‘데이터 페칭에 SWR과 react-query가 대표적인데, 둘 중 react-query를 선택한 이유가 있냐?’는 질문을 받기도 했는데요. 저는 일정을 지키려면 여기서는 조금 더 익숙한 라이브러리를 선택하는 게 좋겠다고 판단했고, 그래서 react-query를 사용했습니다.

에러 핸들링

에러 처리에 대한 고민 또한 기술 요구사항에 포함되어 있었습니다. 먼저 발생할 수 있는 에러를 나열해봤습니다.

  • API 요청에서 발생하는 에러
    • 쿠폰 목록 조회 요청
    • 배너 조회 요청
    • 쿠폰 다운로드 요청
  • 예측하지 못한 에러

그리고 각 케이스에 어떤 에러 처리가 필요할지, 어떤 에러 상태 UI를 제공할지 고민했습니다. 정리한 에러 상태 UI를 기준으로 다시 정리했을 때, 크게 두 가지로 나눌 수 있었어요. 초기 화면 렌더링에 영향을 미치는 에러, 그리고 초기 렌더링 후에 발생하는 에러인데요.

  1. 먼저 초기 렌더링에 영향을 미치거나 예측하지 못한 에러의 경우, 다음과 같이 전역을 감싸는 에러 바운더리를 이용해 에러 페이지 UI를 제공하려 했습니다. 이때 사용자가 직접 새로고침 할 수 있는 UI를 제공해야겠다고 생각했고요.

    function App() {
      return (
        <ErrorBoundary fallbackRender={<ErrorPage />}>
          <QueryClientProvider client={queryClient}>
            ...
            <Suspense fallback={<LoadingPage />}>
              <VIPLoungePage />
            </Suspense>
            ...
          </QueryClientProvider>
        </ErrorBoundary>
      );
    }
  2. 다음은 쿠폰 다운로드 요청과 같이, 화면 진입에 성공한 후 API에서 발생하는 에러입니다. 이 경우에는 이미 렌더링 된 화면에서 사용자가 동작을 다시 시도할 수 있기 때문에, 전체 페이지를 바꾸지 않고 토스트안내 메시지를 띄우고자 했습니다.

    const { mutate: downloadCoupon } = useMutation(
      () => postQuestComplete(promotionCode, id),
      {
        onError: (error) => {
          ...
    
          if (errorStatus && (isError500 || isFaileCouponIssue || isExceedLimitCountIssue)) {
            openSnackbar(errorResponse?.message);
          } else if (errorStatus && (isNetworkConnectionIssue || isAxiosTimeoutError)) {
            openSnackbar("네트워크 연결에 문제가 있습니다. 와이파이 또는 데이터 연결을 확인해주세요.");
          } else {
            openSnackbar("쿠폰을 발급하지 못했어요. 잠시 후 다시 시도해주세요.");
          }
        },
      }
    );

    API 요청에서 발생하는 에러는 react-query의 onError 옵션을 사용해서 처리해주었습니다. 해당 요청에서 발생하는 에러 케이스를 다시 분류하고, 케이스에 따라 안내 메시지를 구분해주었어요.

위와 구상하고 나니 실제 서비스는 에러를 어떻게 다루고 있는지 궁금했습니다. 베타 버전 서비스에서 일부러 에러를 만들며 확인해봤더니, 마침 제가 구상한 것과 동일하게 에러 페이지와 토스트를 제공하고 있었어요. 디자인을 참고하여 아래와 같이 구현했습니다. 😎

스토리북

등급별 혜택 안내 지면에서는 등급에 따라 제공되는 마패 이미지, 색상, 쿠폰이 달라집니다. 등급별 화면 UI를 확인하려면 테스트 계정으로 일일이 로그인하며 확인해야 했는데요.

계속 여러 계정으로 로그인하며 화면을 확인하는 게 불편하더라고요. 🥲 그래서 상태별 UI를 확인할 수 있도록 스토리북을 추가했습니다. 그리고 로컬 환경과 이후 추가될 수 있는 테스트에서도 API 모킹을 재사용할 수 있도록 msw도 함께 추가했습니다.

배포하기

마지막 주에는 배포 파이프라인을 구성해봤습니다. 팀원분의 리드로 파트에서는 어떻게 배포를 진행하는지 큰 틀을 파악했고, 이후 AWS와 젠킨스를 반복해서 둘러보며 익숙해지려고 했습니다.

또 배포 구성을 한번 따라 해본 경험으로는 부족하다고 느껴져, 사내에서 제공하는 AWS 놀이터를 이용해 인프라를 연습해보기도 했어요. 그렇게 직접 개발한 지면을 베타 버전의 어플에 올려 동작을 확인했을 때는 정말 정말 신기했습니다. 😊

직접 배포를 하고 보니, 로컬에서 프로젝트를 돌려볼 때와 웹뷰에서 배포된 번들 파일만 불러 확인했을 때가 달라 수정이 필요한 부분이 있었어요. 번들 파일을 로드하는 html을 직접 제어할 수 없는 상황이라, react portal을 위한 컨테이너 돔 요소는 자바스크립트로 직접 주입하도록 코드를 추가하거나, CSS extract을 사용하지 않고 함께 번들하도록 수정해야 했습니다.

이런 경험과 함께 프록시 툴을 알아보게 되었습니다. 프록시 툴 중 하나인 프록시 맨을 사용해보고, 사용법을 간단히 정리했어요. 프록시 툴을 직접 사용해보니 아주 편했는데요. 필요한 부분을 잘 매핑해두면 웹뷰 상에서 로컬 호스트로 일일이 이동할 필요가 없고, 배포 후의 결과물도 조금 더 정확히 확인할 수 있어 좋았습니다. 👍

코드 리뷰와 함께 배우기

코드 리뷰는 받을 때도 남길 때도 항상 많이 배울 수 있습니다. 그래서 저는 코드 리뷰를 좋아하는데요. 🥰

이번 파일럿 프로젝트를 진행하면서 데모 때마다 MR을 생성하고 코드 리뷰를 받을 수 있었습니다. 언제나 그렇듯 이번 코드 리뷰에서도 많이 배우고 놓친 부분들을 챙길 수 있었습니다.

배민 내 웹뷰에서 브라우저 지원 범위는 어디까지 챙겨야 하는지 확인할 수 있었고,

놓친 접근성과 시멘틱 태그도 다시 챙길 수 있었습니다.

리뷰를 적용하면서 모호한 부분은 다시 질문드리기도 했고요.


폴더 구조와 파일명에 관해 이야기 나누기도 했습니다.

외에도 실무에서 챙겨야 할 많은 부분을 많이 배울 수 있었습니다.
꼼꼼하고 친절한 리뷰 남겨주신 파트 분들께 다시 한번 감사드려요! 👍👍👍

파일럿 프로젝트를 마치며

이렇게 한 달간의 프로젝트가 끝났습니다! 👏👏👏

좋았던 점과 아쉬운 점을 돌아보면서 마무리하려 합니다.

좋았던 점은 너무나도 많은데요. 웹뷰 개발은 처음이었는데, 앱과의 인터렉션, safe-area 등 새로운 것들을 경험할 수 있어서 좋았고요. 이번 적응기를 통해 팀원분들과 소통하고 알아갈 수 있었던 것도 좋았습니다.

세 번의 데모와 코드 리뷰를 진행했는데요. 진행 상황을 잘 공유하고, 필요한 질문을 잘하려고 노력했습니다. 그래야만 제가 모르고 있는 부분들을 정확히 확인할 수 있다고 생각했거든요. 데모마다 같이 공유하면 좋을 내용이 있는지 팀원분들께 여쭤보고, 슬라이드를 준비해서 공유했어요. 작업 상황과 함께 새로 접한 내용들을 ‘얼마나 어떻게 이해하고 있는지’ 그리고 프로젝트를 구성하고 코드를 작성할 때 ‘왜 이런 방식을 선택했는지’를 전달하려고 노력했는데 충분했는지 모르겠습니다. 발표와 리뷰마다 칭찬도 많이 해주셔서 더 잘 해봐야지 다짐할 수 있었고, 또 제가 놓친 부분들을 잘 짚어주셔서 많이 배우고 다시 생각해볼 수 있었어요. 덕분에 재미있었습니다.

아쉬운 점은 사실 없는데요! 아쉽다기보다 제가 더 공부하고 채워야겠다고 생각했던 부분은 아래 두 가지입니다.

  1. 테스트 코드를 언제 얼마나 짜는 게 적절한지에 대한 고민
  2. 배포 파이프라인과 CI/CD에 능숙해지기

이번 요구사항에서 테스트는 선택이라 시간상 스토리북 정도만 추가했는데, 요구사항을 기반으로 테스트 명세를 작성해서 컴포넌트 단위 테스트도 했으면 더 좋았겠다는 생각이 들었습니다. 또 전반적인 배포 흐름은 파악했지만, 아직 배포에 익숙하다는 느낌은 부족했어요. 앞으로 더 경험하고 알아갈 수 있을 것으로 생각합니다. 😁

글을 마무리하면서 팀원분들, 특히 웹프론트 파트 팀원분들께 감사하다는 말씀 꼭 드리고 싶습니다.

으쌰으쌰 응원도 정말 많이 해주시고, 편하게 질문하고 작업할 수 있도록 격려해주셨어요. 덕분에 지난 기간 동안 무사히 적응할 수 있었습니다. 좋은 분들과 함께 할 수 있게 되었다는 생각과 감사한 마음이 많이 들어요. 🙇 (배민푸드서비스개발팀 짱! 웹프론트 짱! 😆)

긴 글 읽어주셔서 감사합니다!


참고 자료