쏘카플랜 개편기

대부분의 개발자들은 논리적 사고와 정교한 설계를 통해 체계적으로 멋진 제품을 만드는 것을 목표로 합니다. 하지만 현실에서는 마감일에 쫓겨 우선순위를 정하고, 기능만 동작하도록 급히 코드를 작성해야 하는 상황에 처할 때가 많습니다.

다음과 같은 분들이 읽으면 좋습니다.

  • 모든 프론트엔드 개발자
  • 코드 품질과 마감일 사이에서 고민 중인 개발자
  • 쏘카플랜에 관심 있는 바로, 여러분

쏘카플랜 개편

쏘카에서는 시간 단위의 카셰어링 서비스뿐만 아니라 한 달 단위로 차량을 빌릴 수 있는 쏘카플랜 서비스를 운영하고 있습니다. 쏘카플랜은 초기 비용 제로, 새차 대여, 정비, 주유, 세차, 내일 배송 등 사용자 맞춤 서비스를 제공하며 2020년 첫 출시 이후 꾸준히 성장해 왔습니다. 이번 여름, 사용자들의 긍정적 반응에 따라 서비스를 확장하고 편의성을 높이기 위해 개편 작업을 진행했는데요. 여러 피드백과 데이터를 수집하여 기획된 주요 개편 내용은 다음과 같았습니다.

  • 한 달 단위로 계약을 자동 연장할 수 있는 구독 기능 추가
  • 디자인 개편
  • 차량 필터 기능 강화
socarplan_renewal_main_page_image

이거 개발 얼마나 걸릴 것 같아요?

개발자로서 “이거 개발 얼마나 걸릴 것 같아요?”라는 질문에 대답하기는 매번 쉽지 않습니다. 기획, 디자인이 완성된 상태에서 개발 일정을 예측하기도 어렵지만 와이어프레임만 보고, 심하면 몇 마디 만으로 그 자리에서 답을 내야 하는 경우도 종종 있습니다(더 심한 경우 일정 연기에 대한 모든 책임을 떠맡을 수도…). 특히나 프론트엔드 개발자라면 기획, 디자인, 서버 개발과 모두 엮여 있기 때문에 예측이 더 어려울 수밖에 없습니다. 이번 프로젝트도 일정 산출을 하는 것으로부터 시작되었습니다.

초기 디자인

초기 디자인

“이 정도 둘이서 개발하는데 얼마나 걸릴 것 같아요?”라는 질문을 받고 아래 내용을 고려해 나름대로 머리를 굴려보았습니다.

  • 프론트엔드 개발자 2명
  • 페이지 4개 리뉴얼
  • React v18, Next.js v13 적용 및 코드 마이그레이션 작업
  • 참고할 수 있는 기존 코드 존재

“배포까지 3주면 충분히 할 수 있을 것 같네요”라고 자신있게 대답했습니다. 하지만 개발을 시작한지 3일이 채 되지 않은 시점에 제 예측이 완전히 틀렸다는 사실을 깨달았습니다. 점진적으로 추가되는 요구사항과 로직 복잡도, 디자인에 따른 최종 구현 범위는 제 예상을 크게 뛰어넘었습니다.

최종 디자인

최종 디자인

결국 힘을 숨기고 있던 기획, 디자인, API에게 정신없이 치이며 매일 야근을 할 수밖에 없었습니다. 프론트엔드 개발자로서 초기 단순한 화면부터 최종 구현 범위를 어느 정도 예측해야 했지만 그러지 못한 결과였습니다. 게다가 기획 중 놓친 부분이나 생각지 못한 에러를 발견할 때마다 수정해야 할 작업들은 계속해서 추가되었고 코드 품질은 떨어져만 갔습니다.

개발 일정 오차를 줄이려면

때때로, 일정을 가늠하기 어려운 상황에서 저처럼 단순히 머리속에서 계산한 값으로만 일정을 정하는 경우가 있습니다. 누구도 시키지 않았지만 그 자리에서 바로 답을 내야 한다는 생각에 사로 잡혔기 때문입니다. 그러나 개발 일정 산출은 다른 팀원과 회사와의 약속입니다. 팀원들도 빠른 답변보다는 정확한 답변을 원할 것입니다. 여유를 가지고 생각할 시간을 두는 연습이 필요합니다.

정확도를 높이기 위해 우선 측정이 필요합니다. 많은 회사나 팀에서는 실제 업무에 소요된 시간을 측정하거나 스토리 포인트와 같은 단위를 사용하여 일의 속도나 난이도를 측정하고 있습니다. 저희 팀에서도 2주 단위의 스프린트와 함께 각 업무에 대한 스토리 포인트를 계산하고, 스프린트 회고 시간에 스토리 포인트의 척도를 조정하는 방식의 추정 프로세스를 진행하고 있습니다.

여기서 한 발 더 나아가, 팀 회고 시간이 끝난 후 개인적으로 회고 내용을 정리하는 습관을 들이면 개발 일정 오차를 줄이는 데 큰 도움이 됩니다. 우선 작업의 성격과 우선순위에 따라 카테고리화 하는 것이 좋습니다. 먼저, 자주 하는 작업을 골라보세요. 프로젝트에서 반복적으로 수행되는 작업으로, 자주 발생하는 에러나 문제점을 포함할 수 있습니다.

실전 코드 작성 팁

DRY VS WET

DRY(Don’t Repeat Yourself) 원칙은 동일한 코드를 반복하지 말라는, 가장 유명한 개발 원칙 중 하나입니다. 코드의 중복은 대표적인 안티 패턴으로 알려져 있습니다. 동일한 코드가 반복될 경우 코드 변경 시 모든 코드를 일일이 수정해야 하고 이 과정에서 실수가 발생할 수 있어 잠재적 버그의 위험성을 높이기 때문입니다. 또한 유지보수에 대한 부담이 커지고 더 많은 시간을 소요할 수 있다는 문제점이 있습니다. 코드 리뷰에서도 항상 등장하는 주제이며, 코드 반복에 대해 강박적으로 불편함을 느끼는 사람도 존재합니다.

코드 중복을 꼭 안티 패턴이라고 생각해야 할까요? DRY 원칙과 반대되는 WET(Write Everything Twice) 원칙은 추상화가 적절하게 이루어지지 않으면 반복되어지는 코드보다 더 큰 비용이 든다고 여기는 원칙입니다. Redux를 개발한 Dan Abramov가 WET Codebase라는 주제로 발표한 프레젠테이션을 통해 조금 더 자세하게 설명드리겠습니다.

(1) (1) 빨간색, 보라색 두 가지 모듈이 있습니다. 일반적으로 두 개의 모듈에 공통 로직이 있다면 DRY 원칙에 따라 중복을 제거하고 새로운 노란색 모듈을 만들 것입니다.

(2) (2) 노란색 모듈을 만들어 빨간색, 보라색 모듈이 노란색 모듈을 의존하게 만들었습니다. 이후에 추가로 노란색 모듈과 비슷한 기능이 필요한 파란색 모듈이 나타납니다. 기능적으로 약간 차이가 있어 바로 가져다 사용할 수는 없지만 DRY 원칙에 따라 노란색 모듈을 수정해 모듈을 통합합니다.

(3) (3) 노란색 모듈이 처음 의도와는 조금 다르게 변했습니다. 하지만 코드 중복을 피했으니 큰 문제는 없다고 판단합니다.

(4) (4) 시간이 흐르면서 기능이 추가되거나 버그를 발견할 때마다 예외 케이스가 하나 둘 씩 추가됩니다.

(5) (5) 코드를 통합할 때는 생각하지 못했던 새로운 코드가 늘어나기 시작합니다. 처음에는 완전히 같은 코드가 필요하다고 생각했지만, 모듈 간 미세한 차이로 인해 노란색 공통 모듈은 더 이상 무엇을 나타내는 코드인지 파악하기 어렵습니다.

결과가 이상하게 느껴질 수 있을 것입니다. 하지만 이런 과정은 조금씩 점진적으로 이루어지기 때문에 중간 과정에서는 문제를 파악하기 어렵습니다. 특히 수많은 개발자가 협업하는 과정에서는 코드가 거대해지며 맥락을 파악하기 점점 힘들어지고, 모르는 사이에 기능들이 추가되기도 합니다.

코드 중복은 장기적 관점에서 좋지 않지만 잘못된 추상화 또한 장기적 관점에서 좋지 않을 수 있습니다. 따라서, 처음부터 코드 중복을 피하기 위해 추상화를 하기보다는 코드를 시간을 두고 관찰할 필요가 있습니다. 어느정도 시간이 흘러 코드가 안정화되었다고 판단할 때, 이전과 다른 공통점을 추출할 수도 있습니다. WET(Write Everything Twice) 원칙은 코드의 중복을 피하기 위해 추상화를 하지 말라는 것이 아닙니다. 상황에 따라 알맞는 원칙을 이해하고 적용하는 것이 중요합니다.

코로케이션(Colocation)

프로젝트를 새로 시작할 때마다 고민되는 주제로 폴더 구조가 있습니다. 리액트 프로젝트는 container-presenter pattern, ducks pattern, hooks-component pattern, atomic pattern 등 폴더 구조를 나타내기 위한 대표적인 몇 가지 패턴이 있습니다. 만약 팀의 컨벤션이 존재하거나 프로젝트와 잘 맞는 패턴이 있다면 일단 그 구조를 따르면 됩니다. 그렇지 않은 경우 어떤 구조를 가져갈지, 컨벤션을 어떻게 맞출지 등 여러 가지 요소를 고려해야 합니다. 리액트 공식 홈페이지(구버전)에서는 파일 구조에 대해 5분 이상 시간을 투자하지 말라고 안내하고 있습니다. 모든 파일을 하나의 폴더에 보관하는 방법으로 우선 시작하고, 프로젝트가 충분히 커지면 일부 파일을 나머지로 분리해 보관하는 것을 권장합니다. 그 시점까지 자주 함께 변경되는 파일들을 같이 보관하는 것이 좋으며, 이러한 원칙을 ‘코로케이션(colocation)’이라고 부릅니다.

쏘카플랜에 코로케이션과 WET 원칙 적용해보기

쏘카플랜은 처음부터 폴더를 세분화하지 않고, 충분히 기다렸다가 프로젝트 특성에 맞는 구조를 가져가는 전략을 취하기로 결정했습니다. 앞서 언급한 WET 원칙과 코로케이션에 따라 구조를 가져간 예시를 통해 자세히 설명하겠습니다.

두 이미지는 각각 차량 리스트 화면과 필터 화면입니다. 화면 구현을 위해 먼저 코로케이션에 따라 페이지 별로 폴더를 나누고 각 화면에서 사용되는 component, context, recoil state, type guard, hook 등의 파일을 한 폴더에 담았습니다.

두 화면에서 공통적으로 나타나는 컴포넌트 UI를 볼 수 있는데요. 바로 혜택 태그 필터 섹션입니다. 필터의 혜택 섹션과 리스트의 혜택 섹션은 정확히 같은 기능을 담당하며 둘 중 한 곳의 상태가 변경되면 다른 곳도 똑같이 상태가 변경되는 형태를 가지고 있습니다.

(A)
(B)

하지만 두 태그 리스트 간에는 조금 다른 부분들이 존재합니다. 먼저, 각진 모양과 둥근 모양으로 UI로 구분됩니다. 또한 A 필터는 횡스크롤 형태로 나타내야 하고, 선택된 태그가 스크롤에 의해 가려져 있다면 스크롤을 이동시켜야 합니다. 그밖에 각 페이지 내에서 상호작용하는 API 및 컴포넌트 간 동작이 조금씩 다를 수 있습니다.

이런 경우에는 흔히 코드 중복을 없애기 위해 하나의 CarTagList라는 컴포넌트를 생성하여 관리하는 방법을 선택합니다. 예를 들어, 하나의 컴포넌트에서 type과 같은 props를 받아 list typefilter type으로 나누어 두 가지 형태를 나타낼 수 있습니다. type props로 list를 받는다면 횡스크롤 형태의 둥근 버튼 UI를 나타내고, filter를 받으면 각진 버튼으로 나타내는 것입니다.

// 예시를 위해 간소화한 코드입니다.

interface Props {
  type: 'list' | 'filter'
}

const CarTagList = ({ type }: Props) => {
  if (type === 'list') {
    return (...)
  }

  if (type === 'filter') {
    return (...)
  }
}

하지만 시간이 지난 후, 새롭게 추가되는 디자인 화면에 각진 버튼에 횡스크롤하는 UI가 나온다면 어떻게 될까요? type으로만 구분할 수 있던 컴포넌트의 type 속성을 제거하고 buttonType, scrollType을 새로 만들어 각 속성을 구분하여 나타내야 할 것입니다.

interface Props {
  buttonType: 'rounded' | 'angular'
  scrollType: 'overflow' | 'wrap'
}

const CarTagList = ({ buttonType, scrollType }: Props) => {
  if (buttonType === 'rounded') {
	(...)
  }
  if (buttonType === 'angular') {
    (...)
  }
  if (scrollType === 'overflow') {
    (...)
  }
  if (scrollType === 'wrap') {
    (...)
  }
}

여기서 추가로, 버튼 클릭 동작에 따른 에러가 발견될 경우 type 별로 나누어 예외처리를 해야 하는 상황이 올 수도 있습니다. 혹은 둥근 버튼일 때만 동작하는 새로운 기능이 추가되기도 합니다. 이렇게 시간이 지나면서 기능이 추가되거나 예외가 발생한다면 로직은 점점 복잡해지고 처음 의도했던 컴포넌트의 모습과는 거리가 멀어집니다.

WET 원칙을 따라 미리 컴포넌트를 추상화하지 않고 어느 정도 안정화된 상태에서 컴포넌트를 분리한다면 이 문제를 피할 수 있습니다. 저는 WET 원칙에 따라 A에서 사용된 코드를 B로 복사 붙여넣기한 후 따로 코드를 작성했습니다. 그리고 시간이 흐른 뒤, 첫 번째 서비스 런칭 이후 안정화된 상태에서 리팩토링을 진행했습니다. 리팩토링은 각 관심사를 분리하는 것으로 시작했습니다. 먼저 태그의 UI만 표현할 수 있는 Tag 컴포넌트를 만들었고, 이 컴포넌트가 오로지 모양과 선택 여부에 대해서만 신경쓰도록 했습니다.


const Tag = ({ id, label, variant, selected, onSelect }: Props) => {
  return (
    <button
      type="button"
      className={classNames(
        'flex items-center justify-center border px-12 py-8 text-14',
        selected
          ? 'border-navy-040 bg-navy-040 text-white'
          : 'border-grey-030  text-grey-050',
		variant === 'rounded' && 'rounded-full'
      )}
      onClick={() => onSelect(id)}
    >
      {label}
    </button>
  )
}

다음으로, 혜택 태그에 관련된 도메인 로직을 hook 형태로 만들어 줍니다. 이 hook은 서버에서 혜택 태그 목록을 가져오고, 기존에 선택된 태그를 초기화하고, 선택된 태그들의 상태를 관리하는 역할만을 수행합니다.

export const useCarTypeTagIdsFilter = () => {
  const { data } = useGetCarTypeTags()
  const [selectedCarTypeTagIds, setSelectedCarTypeTagIds] = useState([])

  useEffect(() => {
    // 태그 필터 초기화
    (...)
  }, [])

  return {
    carTypeTags: data,
    setSelectedCarTypeTagIds,
    selectedCarTypeTagIds,
  }
}

마지막으로 태그의 선택 여부를 관리하는 Context와 함께 만들어둔 Hook과 Tag 컴포넌트를 불러옵니다.

const CarTypeTagSelection = () => {
  const {
    carTypeTags,
    setSelectedCarTypeTagIds,
    selectedCarTypeTagIds,
  } = useCarTypeTagIdsFilter()

  const onSelect = () => {
	(...)
  }

  return (
    <MultipleSelectionProvider
      value={selectedCarTypeTagIds}
      onSelect={onSelect}
    >
      {carTypeTags.map(({ id, name }) => (
         <Tag id={id} name={name} variant="rounded" />
      ))}
    </MultipleSelectionProvider>
  )
}

Tag는 UI 모양만을, 나머지 혜택 태그 관련 로직은 hook을 사용하여 관심사를 분리했습니다. 이 과정에서 컴포넌트 간 결합도를 낮추었기 때문에 Tag가 아닌 다른 컴포넌트와 조합하여 사용하거나 다른 hook을 만들어 사용하기에도 편리합니다.

또한 리팩토링 과정에서 혜택에 관련된 도메인 로직을 분리할 때 features라는 폴더를 만들어 도메인 별 로직을 한 곳에서 관리할 수 있도록 했습니다. carTypeTagIds 폴더에 관련 파일을 모아서 관리했는데, 이처럼 도메인에 따라 폴더를 나누는 패턴을 feature-driven component 패턴이라고 합니다.

스크린샷 2023-10-06 오후 3.47.08.png

앞선 예시는 간단한 사례라 숙련된 개발자라면 첫 단계부터 제대로 추상화를 했을 것입니다. 그러나 컴포넌트나 화면 구성이 복잡할수록, 기획과 디자인의 변경이 잦을수록, 미리 모든 걸 예상해서 로직을 분리하는 일은 어렵습니다. 이럴 때는 충분한 시간이 지나고 코드가 안정화됐을 때 추상화해 폴더 및 파일을 분리하는 방식을 사용하는 게 도움될 수 있습니다.

Storybook 활용

프론트엔드 개발자들 간의 협업에서는 종종 같은 컴포넌트를 중복해서 만드는 경우가 있습니다. 컴포넌트의 존재를 모르고 새로 만드는 경우도 있고, 알고는 있지만 컴포넌트 사용법을 몰라서 사용하지 않는 경우도 종종 있습니다. 서버 개발자와 프론트엔드 개발자 간에 API 명세서가 필요한 것처럼, 프론트엔드 개발자들 간에도 컴포넌트 사용법에 대한 명세가 필요합니다.

쏘카플랜 스토리북

쏘카플랜 스토리북

QA에 대처하는 자세

배경

개편 작업의 MVP(Minimum Viable Product, 최소 기능 구현 제품)를 메인 화면, 차량 리스트, 차량 필터, 차량 상세 4가지로 크게 나누고 환경 세팅을 시작으로 작업에 착수했습니다. 이미 존재하는 화면까지 새로 개발하기에는 기간이 너무 촉박했기 때문에, 이들은 최대한 이관하는 것으로 방향을 잡았습니다. 그러나 변경되는 기술 스택이 있었기 때문에 코드를 그대로 이관하기는 쉽지 않았습니다.

QA 주간에 발견된 이슈 개수
군생활 때 눈 치우는 것처럼 어? 치웠는데 또 있네? 느낌입니다

쏘카플랜의 런칭 일정은 6월 21일이었는데, QA/QC 진행 또한 6월 15일부터 20일까지 과중한 일정이었습니다. 촉박한 개발은 단지 몸이 축나고 피곤하다는 결과만을 초래하지 않았습니다. 코드베이스의 안정성 또한 크게 떨어진다는 것을 처음으로 실감했습니다. 단, 며칠 간의 품질 검증으로 130개의 이슈가 발행되었습니다(거의 30~40개 / 1day).

이 중 10건 가량이 크리티컬한 이슈였는데, 이번에 개발한 범위와 이관한 범위에서 골고루 발생되었습니다.

대응 과정

  1. 각기 담당한 프론트엔드 업무 별로 발행되는 이슈에 대응한다.
  2. 치명적인 결함 → 주요 결함 → 일반 결함 → 사소한 결함 순으로 우선순위를 나누어 대응한다.
  3. 높은 우선순위의 이슈를 대응하며 동시에 수정이 가능할 결함들은 동시에 진행한다.
  4. 반영이 가능한 상황에 최대한 빠르게 반영한다.
크런치 주간의 github 잔디 색상
QA 주간의 크런치 모드 (마지막 주는 정말…)

특히 어려웠던 상황

  1. 결함을 해결하기 위해 수정했지만 이것이 또 다른 부수 효과를 발생시켜 새로운 결함이 만들어지는 상황
  2. 이관한 코드에서 발생된 결함
  3. QA를 진행하며 개발해야 하는 새로운 기능
뭉크의 절규

첫 번째 경우는 ‘이거 치명적인데, 빨리 고쳐야 하는데’ 같은 조급함이 시야를 좀먹는 상황이었습니다. 좀 더 여유 있고 침착하게, 작성한 프로그램을 고찰했다면 금방 해결했을 문제라고 생각합니다. 조급하더라도 침착해야 합니다.

두 번째 경우는 코드 복잡도에 따라 다르겠지만, 저희가 이관한 이용내역 은 코드 복잡도가 꽤 높았습니다. 계약 연장이나 차량 반납 같은 순차적인 화면 로직 상태를 추적하기 굉장히 어려웠습니다. 이것을 차근차근 분석할 시간이 없었기 때문에 우선 문제가 발생했을 때 해결하는 것에 집중했고, 백로그화해 우선 순위를 나누었습니다.

세 번째 경우는 새로운 일감이 추가로 들어오는 경우인데, 이 때 한 쪽에 집중력을 온전히 쏟다가 다른 일을 누락하는 경우가 있을 수 있습니다. 이럴 때는 To do list를 명확히 하는 것이 중요했습니다. 내가 할 일을 단지 나열하는 것 뿐만 아니라, 참여해야 할 다른 스케쥴이 있다면 오늘 개발할 수 있는 시간을 보수적으로 측정하여 갑작스런 상황에 대처할 수 있어야 합니다.

개발할 시간이 거의 없는 날이 꼭 있습니다

개발할 시간이 거의 없는 날이 꼭 있습니다

레거시 코드 개선

코드 개선은 제품의 성능을 향상시키는 것과 유지보수하기 편리한 코드로 만드는 작업입니다. 두 가지를 모두 양립하면 가장 좋겠으나 쉽지 않습니다.

환경 구성

먼저 환경을 구성할 때 eslintprettier 를 채용해 일관된 코딩 패턴을 사용했습니다. 또한 정적 타입 체커인 TypeScript를 사용하여 서버와의 인터페이스를 확인하기 편리했습니다. 타입스크립트를 사용하면 함수, 변수들의 의도가 명확해지기 때문에 코드를 더 읽기 쉽게 만들고, 다른 개발자와 협업할 때 유지보수에도 큰 도움이 됩니다. 프론트엔드에서 타입스크립트의 효율성은 더 설명할 필요도 없을 정도입니다!

레거시 코드

앞서 설명했듯이 이용내역 화면은 레거시 코드였습니다. 레거시 코드 자체가 나쁠 이유는 없습니다. 이미 기존에 비즈니스 로직을 잘 수행하고 있기 때문입니다. 그러나 다음과 같은 이유로 레거시 코드를 개선하기로 결정 했습니다:

이를 개선하려는 이유는 다른 이유였습니다:

  1. 상태에 대한 결합도가 높아 다소 복잡하고 디버깅이 어려움
  2. 새로운 요구사항에 대응하기 위해 기능을 확장할 때 수용하기 어려움
  3. 성능이 향상된 최신 기술을 반영하기 어려움

이를 위해 레거시 코드가 영향을 미칠 수 있는 부분을 면밀히 검토하고 있고, 브랜치를 따서 짬짬이 개발하고 있습니다. 충분한 테스트는 필수 조건입니다. 한 번에 모조리 바꾸려 하지 않고 점진적으로 바꿔나갈 계획입니다.

레거시에는 개선할 수 있는 다양한 것들이 있었습니다.

  • 번들 사이즈 축소: 같은 기능이라면 경경익선(輕輕益善; 가벼우면 가벼울수록 좋다)
  • any 타입 제거: any 타입이 많다면 확장자만 .ts 인 ECMAScript겠죠?
  • 추상화, 공통 로직 모듈화 : 위에서도 언급했지만 추상화가 필요한가? 공통화가 필요한가? 라는 선제 검토가 필요합니다.
  • 처음부터 다시 작성 : 일부 레거시 코드는 수정하기 너무 어려울 수 있습니다. 이 경우에는 코드를 처음부터 다시 작성하는 것이 최선일 수 있습니다. 최후의 수단이지만 유일한 해결책일 수도 있습니다.
    처음부터 다시 시작할 수 있다면 책 표지
    다시 출발할 수 있다면.. 더 나아질까요…?

일하는 방식 개선

쏘카 개발본부는 목적조직(팀)으로 이루어져 있고, 목적조직에서 담당하고 있는 도메인들이 있어 그를 주요하게 개발합니다. 프로덕트 본부에서 기획, 디자인을 꼼꼼히 만들어주신다면 저희는 제품으로 만들어 사용자에게 출시하는 작업을 합니다. 보통 이런 구조로 프로세스가 이루어집니다.

하지만 위의 경우 조직의 규모가 커질수록 소통이 빠르게 이루어지기는 점점 더 어려워집니다. 의사결정을 기다리거나, 개발된 기능이 언제 배포되는지 기다리거나 하는 비효율이 발생할 수밖에 없습니다. 그런 프로젝트를 위하여 쏘카는 버킷이라는 단위로 프로젝트 안에서 소통합니다. 제품과 관련된 모든 기여자(비즈니스, 프로덕트, 엔지니어)가 플랜 버킷이라는 이름으로 똘똘 뭉쳐 있는 조직입니다. 이렇게 되면 빠른 소통이 가능해져 애자일한 프로세스가 비로소 완성됩니다.

플랜은 1주 단위로 스프린트를 진행하고 있기 때문에 매주 월요일에 위클리를 진행하고 있습니다. 여기서 쏘카플랜에 대한 현황을 사업팀에서 공유해주십니다. 이번 주 계획과 백로그를 공유하고 우선순위를 정해 미룰 건 미루고 당장 할 수 있는 것을 기탄없이 논의하고 있습니다.

또한 각자의 역할에서 충실히 업무를 수행하고 계시지만 버킷 멤버들이 어떻게 생각하고 어떻게 일하는지를 알게 된다면 서로 협업하는데 더욱 도움이 될 거라 생각하여 플랜 버킷 워크숍도 개최했습니다. 팀워크를 위해 친해지는 시간을 먼저 가지고 서로에 대해 공감할 수 있었고, 풍부한 아이데이션을 통해 우리의 비전도 확인할 수 있었습니다.

회고, 요약, 마무리

무한도전 이미지

여기까지 저희가 쏘카플랜을 개편하는 중 겪었던 이모저모를 정리해보았습니다. 역량에 비해 무리한 도전이었으나 그 과정에서 역량이 향상되었음은 분명합니다. 특히 고정된 일정에 맞춰 개발 속도를 최대한으로 높여 자신의 한계를 시험할 수 있었던 점은 지나고 보면 재밌기도 했습니다.

높은 품질의 어떤 소프트웨어라도 결코 완전해질 수는 없다고 생각합니다. 에러나 버그가 아직 발생하지 않았을 뿐 언젠가는 있을 수 있습니다. 어느 누구나 결함을 만들 수 있습니다. 에러를 발생시키는 것을 두려워하지 마세요. 중요한 것은 (저처럼) 올바른 자세로 피드백을 경청하고 더 나은 프로그램을 만드는 것입니다. 일어날 수 있는 많은 경우를 작성하고 철저하게 테스트하여 최소로 줄여나가는 것이 나아가야 할 방향일 것입니다.

앞으로도 성장을 추구하며 쏘카에 기여하고, 나아가 프론트엔드 생태계에 도움을 줄 만한 인재가 되는 것이 목표입니다. 함께 무리한 일정을 소화한 플랜 버킷 모두에게, 글을 읽어주신 모든 분들께, 감사합니다.

쏘카플랜 많이 사랑해주세요.