이 글은 우아한테크코스 백엔드 6기 초코칩, 러쉬에 의해 작성되었습니다.


협업 배경

이번 3차 스프린트의 공통 요구사항에 다음과 같은 내용이 있었습니다.

FE에서 BE까지 관통하는 업무 중 최소 하나를 FE / BE 개발자가 짝으로 진행

팀 크루루는 요구사항 충족과 서로 간의 업무를 이해하기 위해 여러 협업을 시도했습니다.

Figma 와이어프레임 디자인 및 컴포넌트 작업

첫 번째로 시도한 협업은 와이어프레임 디자인과 컴포넌트 작업입니다. 기존에도 와이어프레임 디자인에 대해 함께 토의했지만, 이번에는 Figma를 이용해 직접 참여했습니다. BE팀 러쉬 가 FE 팀과 함께 Figma 작업에 참여했습니다.

Figma가 익숙하지 않은 BE 팀을 위해 FE 팀의 렛서가 Figma 사용 가이드를 제작해주었습니다. 가이드를 통해 기본적인 도형, 텍스트 추가부터 컴포넌트 등록까지 실습할 수 있었습니다.

실습 이후, 직접 와이어프레임과 컴포넌트 등록을 진행했습니다. 처음 해보는 작업이라 모르는 게 많았습니다. 특히 프레임 단위로 요소들을 관리하는 부분이 헷갈렸습니다.

예를 들어, 캘린더 박스의 각 요소들이 하나의 레이어에 있는 것처럼 보이지만, 실제로는 요소들이 레이어별로 나누어져 있습니다. 따라서 요소가 어떤 레이어에 위치하는지에 따라 다르게 보입니다. 요소가 많아질수록 레이어가 복잡해졌고, 관리하기 힘들었습니다.

컴포넌트를 직접 만들어보니, 이 과정이 꽤 번거롭고 시간이 많이 소요된다고 느꼈습니다. 이를 통해 FE 개발이 생각보다 많은 시간과 노력을 요구하는 작업임을 깨달았습니다. 이런 작업은 BE 팀원들도 최소한의 학습만으로 수행할 수 있을 것이라는 생각이 들었습니다.

FE 팀의 인원이 적어 작업 속도가 느려지는 상황을 개선하기 위해, BE 팀원들이 이러한 작업에 도움을 준다면 전체 팀의 효율성이 크게 향상될 것이라 생각했습니다. 이번 경험을 통해 FE 작업의 복잡성과 어려움을 더 잘 이해할 수 있었던 유익한 기회가 되었습니다.


모집 공고 및 지원 폼 조회 기능 구현

모집 공고 및 지원 폼 API의 BE 파트는 초코칩과 FE 파트는 렛서가 맡았습니다.

기능 구현 전에 기능에 대한 흐름에 대해 이야기를 나눴습니다. 기능의 Flow는 다음과 같습니다.

  1. 지원자가 특정 링크를 통해 모집 공고를 조회합니다.
  2. 지원하기 탭 또는 버튼 클릭 시, 지원 폼의 질문을 확인할 수 있습니다.
  3. 지원자는 지원 폼을 통해 제출할 수 있습니다.

왼쪽 페이지는 모집 공고이고 오른쪽 페이지는 지원 폼 질문 조회입니다.

저희가 먼저 고려한 기능은 1, 2번 조회 기능입니다.

해당 기능의 API 설계에서 의견이 갈렸습니다.

피그마의 와이어프레임을 바탕으로 BE팀에서는 하나의 API로 모집 공고와 지원하기 페이지 관련 정보들를 제공하기로 했습니다. 모집 공고와 지원하기 페이지가 z-index를 활용한 모달 형태로 구현될 것으로 예상했기 때문에, API를 2개로 분리하는 것이 불필요하다고 생각했습니다.

하지만 FE팀에서는 공고와 지원하기 탭이 서로 다르기 때문에 때문에 아래와 같이 모집 공고 API와 지원 폼 질문 조회 API를 분리하여 설계할 것으로 기대했습니다.

합의를 위해 렛서와 논의한 점은 다음과 같습니다.

  • 두 개의 API로 구현: 비슷한 기능을 구현하고 있는 당근 채용 페이지에서도 한 번의 API 호출로 구현중입니다. 굳이 두 개의 API로 구현할 필요성을 느끼지 못했습니다.
  • 컴포넌트의 관심사 분리: 공고 탭에서는 지원서 데이터를, 지원서 탭에서는 공고 데이터를 알 필요가 없습니다. 이를 분리할 방법을 찾아야 했습니다.

FE 팀에서 하나의 API로 두 가지 데이터를 모두 제공하되, 서로 다른 탭에서 각각의 관심사에 맞는 데이터만을 알도록 리팩토링했습니다.

  • 전역 상태 관리: react query의 캐시 기능을 사용해 페이지 간 데이터를 공유하게 구현할 수 있었습니다.

  • 컴포넌트가 DTO를 모르게 하기: 실제로는 같은 api query 훅을 사용하지만, 중간 레이어를 두어 컴포넌트에서는 이를 모르도록 추상화했습니다.

      const useGetRecruitmentInfo = ({ applyFormId }: { applyFormId: string }) => {
      const TEN_MINUTE = 1000 * 60 * 10;
      const queryObj = useQuery({
        queryKey: [QUERY_KEYS.RECRUITMENT_INFO, applyFormId],
        queryFn: () => applyApis.get({ applyFormId }),
        staleTime: TEN_MINUTE,
        gcTime: TEN_MINUTE,
      });
    
      return queryObj;
    };
    
    export const applyQueries = {
      useGetRecruitmentPost: ({ applyFormId }: { applyFormId: string }) => {
        const { data, ...restQueryObj } = useGetRecruitmentInfo({ applyFormId });
        const { startDate, endDate } = data?.recruitmentPost ?? { startDate: '', endDate: '' };
        const isClosed = !getTimeStatus({ startDate, endDate }).isOngoing;
    
        return {
          isClosed,
          data: data?.recruitmentPost,
          ...restQueryObj,
        };
      },
    
      useGetApplyForm: ({ applyFormId }: { applyFormId: string }) => {
        const { data, ...restQueryObj } = useGetRecruitmentInfo({ applyFormId });
    
        return {
          data: data?.applyForm.questions,
          ...restQueryObj,
        };
      },
    };
      

해당 기능을 FE와 BE 팀이 긴밀하게 협업하는 과정에서 여러 가지를 배울 수 있었습니다. 먼저, 소통의 중요성을 느꼈습니다. 각 팀의 요구사항과 제약사항을 명확히 이해하고 조율하는 과정이 매우 중요하다는 것을 알게 되었습니다. 서로의 입장을 이해하고, 최적의 합의점을 찾아가는 과정에서 즉각적인 소통이 큰 도움이 되었습니다.

또한, 유연한 사고의 필요성도 깨달았습니다. 초기 설계 단계에서 나와 다를 수 있는 설계라는 점을 인식하고, 이러한 의견의 간극을 좁히기 위해서는 유연한 사고와 접근 방식이 필요하다는 것을 배웠습니다. 이는 팀원 간 협업을 통해 더 쉽게 이루어질 수 있었습니다.

이번 협업을 통해 FE, BE 팀 모두 성장할 수 있었고, 앞으로의 프로젝트에서도 이러한 협업 방식을 지속적으로 적용해 나갈 계획입니다.