글을 쓰는 시점으로부터 2주 전, 입사 직후부터 차근차근히 준비 중이던 서핏의 인재검색 프로젝트를 드디어 론칭했습니다. 이 프로젝트는 총 5번의 스프린트로 진행되었는데, 각 스프린트가 차례로 진행되지 않고 중간중간 다른 피쳐 작업이 병행되어 5개월 남짓 걸렸던 것 같아요.
이 프로젝트는 저에게 큰 의미가 있습니다. 좋아하는 회사에 입사해서 진행한 첫 프로젝트의 전 과정에 온전하게 관여했다는 점, 이 프로젝트가 향후 우리 팀원들의 거취를 결정할 수 있는 중요한 프로젝트라는 점, 이 프로젝트로 인해 엔지니어로써 여러 소프트 스킬을 키울 수 있었다는 점. 이번 글에서는 이렇게 다양한 의미를 가지는 이 프로젝트에 대해서 회고하는 글을 써보려고 합니다. 이 글을 읽음으로써 소규모 스타트업에서 엔지니어로 일하는 사람은 어떤 종류의 일을 할 수 있고, 그로인해 어떤 배움을 얻을 수 있는지 간접적으로나마 알게 되실 수 있어요.
TL;DR
이번 프로젝트로 인해 프론트엔드 작업에 관한 여러 소프트 스킬을 경험했습니다.
비즈니스 임팩트를 만들어내기 위해서 어떤 일을 할 수 있는지 알게 되었습니다.
프로젝트 개요
프로젝트 배경
서핏은 그동안 디지털 프로덕트를 만드는 IT 계열 직군 종사자분들의 커리어 성장을 만들어내는 것에 주안점을 두고 여러 시도를 해왔었습니다. 아티클 큐레이션이나 커리어 프로필이 그런 시도에 의한 결과물이죠. 그런 시도의 연장선으로, 서핏의 사용자분들이 다양한 커리어 제안을 받아보실 수 있게 하는 프로덕트를 만드는 것이 이번에 진행한 프로젝트입니다.
일종의 채용 플랫폼과 비슷한 기능을 제공한다고 생각하시면 편할 것 같아요. 어찌되었든 기업과 인재를 잇는 큰 틀 안에서 유저스토리가 전개되니까요. 다만 사용자들은 서핏을 이용하면서 구축된 아티클 데이터나 커리어 프로필 데이터를 통해서 더 핏이 맞는 제안을 받을 수 있고, 역으로 기업은 인재 채용에 있어 불확실성을 낮출 수 있다는 장점이 있다는 다소 광고색이 짙은 한 문장을 곁들일 수 있을 것 같아요. 🤭
프로젝트 과정
프로젝트는 총 5개의 스프린트와 약간의 칸반을 섞어서 진행했습니다. 이번 프로젝트에서 스프린트는 각기 다른 관점을 가진 사용자 별 유저스토리로 이뤄져있는 작업 단위입니다. 이번에 만드는 프로덕트의 성격 상 다양한 이해관계자가 존재하는데요, 그로인해 아래 과정으로 스프린트가 진행되었습니다.
sprint 1: 기업사용자의 회원 가입
sprint 2: 일반사용자의 제안 설정
sprint 3: 기업사용자의 프로필 설정, 구성원 관리
sprint 4: 기업사용자의 인재 검색
sprint 5: 플랫폼 관리자의 관리자 페이지
프로덕트 QA나 디자인 디테일 작업은 칸반으로 진행되었습니다. 아시다시피 스프린트는 스프린트 플래닝 미팅이라는 과정을 통해 기간 안에 할 일들을 모두 정리해놓고 작업을 시작하기 때문에, 수시로 체크되는 QA, 디자인 디테일 작업들은 칸반으로 관리되는게 자연스러운 것이라고 생각했습니다.
스프린트 타임라인
sprint 1
첫 작업은 기업용 계정을 만들 수 있게 회원가입 기능을 구현하는 작업이었습니다. 기업용 계정을 만들면 서핏의 비즈니스 관련 기능들을 사용할 수 있게 됩니다. 회원가입 과정은 내 정보 입력 단계와 사업자등록증 업로드 단계로 나눠집니다. 두 단계 모두 단순한 입력 폼들로 구성되어서 구현하는 데에 있어 특별히 문제가 될 부분이 없으리라 생각했는데요. 기능 구현을 다 하고 내부 테스트를 하면서 사용성을 저하할만한 몇 가지 문제를 발견했었습니다.
얼마나 더 입력해야하는지 알려주는 placeholder
아래 사진처럼 사업자등록번호 입력창의 placeholder는 입력한 값을 계산해서 앞으로 얼마나 더 입력해야 하는지 사용자에게 알려주는 기능이 제공되어야 했습니다. 이런 요구사항을 파악하지 못하고 일반 placeholder 속성을 사용해서 구현을 완료했던 실수가 있었습니다. 디자인 시안만 봤을 때는 당연히 placeholder 속성을 사용해 구현하면 되는 줄 알았는데요, 덕분에 서핏이라는 조직에서는 사용자의 경험을 어느정도로 중요하게 생각하는지 느낄 수 있었습니다.
이 기능은 일반 placeholder 속성을 사용해서 구현할 수 없습니다. "000-00-00000"을 absolute로 띄운 뒤, 내용을 포함하는 요소의 width값을 계산해서 placeholder의 왼쪽 마진을 주는 방식으로 구현되었습니다.
폼 영역 키보드로 조작
적어도 폼 영역에서는 사용자와 상호작용하는 요소들이 키보드로 조작이되어야 합니다. 12개의 입력 요소와 4개의 클릭 요소가 있는 이 작업물에는 웹 접근성을 차치하더라도 키보드로 요소들을 조작하는 기능이 제공되면 사용자에게 좋은 입력 경험을 남길 수 있습니다. 개발 당시 저는 그런 부분에 대해서 인식하지 못했었고, 이후에 키보드 조작 기능을 제공하기 위한 작업을 따로 진행했었습니다. 화면을 구현하면서 그런 부분까지 고려했었더라면 비용(시간)을 줄일 수 있었을텐데 말이죠.
이 두가지 문제를 해결하면서 사용성이 좋아지는 것을 몸소 느끼자 만드는 작업물의 디테일에 대한 눈이 높아지게 되었습니다. 그동안 프론트엔드 개발자로서 구현해야하는 로직의 처리, 코드의 재활용성, .. 너무 이런 것들만 신경써왔던 것 같다는 반성을 하게 되었습니다.
sprint 2
생뚱맞는 제안을 연속적으로 받는다면 사용자의 경험은 좋지 않을겁니다. 제안을 받을 의사가 없는데 제안이 온다거나, 특정 유형의 제안만 받고싶다거나, 특정 기업에는 노출되고싶지 않다거나, ... 이런 기능을 구현하려면 사용자는 제안에 관한 어떠한 의사를 표현해야 하는데요. 두 번째 스프린트에서는 일반 사용자가 받을 제안 유형을 설정하는 기능을 구현했습니다.
연속적 이벤트 컨트롤하기
제안 설정 화면에서 값 변동이 감지되면 변동된 설정값들을 저장하는 API를 자동으로 호출합니다. 값 변동의 빈도가 많다면 그 횟수대로 API 호출을 시도할 것이고, 만약 화면에 표현되고 있는 값이 API 응답값에 의존하고있다면 API 호출은 비동기적으로 실행되기에 화면에 표시된 값과 실제 데이터베이스에 입력된 값 사이에 차이가 발생할 수 있습니다. 이런 상황에서는 사용자가 설정한 최종적인 값이 서버로 전달되어야 문제가 해결될 수 있을텐데요. 저는 debounce를 적절한 타이밍으로 이용하면 해결할 수 있을 것이라 생각했습니다.
기민하게 대응할 수 있게 설계하기 #1
제안 설정 화면에는 제안 유형을 선택할 수 있게 라디오 컴포넌트와 체크박스 컴포넌트가 제공됩니다. 사진을 보면 아시다시피 라벨 역할을 하는 부분에 이모지나 아이콘, 부가적인 문장이 필요하다는 것을 알 수 있는데요, 처음에 저는 이 화면을 보고 기존 라디오, 체크박스 컴포넌트에 이미지 에셋과 문장을 받을 수 있게 prop을 추가하는 방향으로 수정했었습니다. 하지만 만약에 이 라벨 부분에 추가적인 다른 데이터가 표시되어야 한다면 어떻게 될까요? 제목과 문장의 위치가 수정된다면? 이미지나 영상이 표시되어야 한다면? 또다시 디자인 컴포넌트를 수정해야 할 것이고 그 수정 작업에 대한 프로덕트 내에 사이드 이펙트 확인이 필요할 것입니다.
코딩할 때는 코드를 수정하는 일만 있는 게 아닙니다. 코딩 전 설계 작업과 작업 이후 사이드 이펙트를 확인하는 과정이 있고, 조금 더 나아가서는 디자이너나 기획자와의 커뮤니케이션 과정이 있을 수도 있죠. 이런 비용을 줄이기 위해서는 작업을 하는 그 시점에 갇히면 안 된다고 생각합니다. 만들고 있는 코드가 미래에 기민하게 바뀔 수 있도록 선택지를 많이 가지게 되어야 한다는 뜻입니다.
결론적으로, 제안 설정 화면의 위 컴포넌트들은 설명 영역이라는 이름의 엘리먼트를 통으로 주입받을 수 있게 수정되었습니다. 설명 영역이 통으로 추상화되었기 때문에 이제 어떠한 모양이더라도 유연하게 대응할 수 있습니다.
sprint 3
세 번째 스프린트에서는 기업용 계정의 정보를 수정할 수 있는 기능, 기업용 계정이 있는 사람만 서비스를 이용할 수 있게 하는 처리를 만들었습니다. 계정 정보 수정 관련해서는 단순 CRUD를 프론트엔드에 붙혀놓은 것이라 이 회고에서 다룰 내용은 없습니다만, 인가된 사용자만 비즈니스 서비스를 이용할 수 있게 하는 작업 과정에서는 남길 내용이 조금 있습니다.
서핏에 비즈니스 서비스를 구현하기 위해서는 아래와 같은 선제조건들이 해결되어야 했었습니다.
비즈니스 관련 기능들에 진입할 수 있는 GNB가 있어야 함
선택한 비즈니스 기능의 하위 기능들을 탐색할 수 있는 LNB가 있어야 함
인가된 사용자만이 비즈니스 서비스에 접근할 수 있어야 함
현재 적용된 요금제 정보가 표시되어야 함
이런 기능들의 구현 방법을 생각하다가 비즈니스 전체 페이지들을 감싸서 비즈니스에 대한 정보를 관리하는 부모 컴포넌트가 있으면 해결할 수 있을 것 같다고 생각했습니다.
기민하게 대응할 수 있게 설계하기 #2
사진에 보이는 것처럼 라우터 뷰를 감싸는 레이아웃 컴포넌트를 만들었습니다. 레이아웃 컴포넌트는 컨테이너 컴포넌트 격으로 다양한 하위 컴포넌트가 여기서 렌더링됩니다. GNB나 LNB 컴포넌트는 비즈니스 서비스를 탐색하기 위한 목적이기에 prop으로 이동할 수 있는 서비스 내/외부 링크, 링크 제목을 받아서 표현하는 간단한 컴포넌트로 구성했습니다. GNB의 오른편에는 현재 로그인된 기업의 이름, 기업 로고, 사용자의 이름이 표시되고 있는데요, 이 부분을 구성할 때는 느낌적으로 어떤 요소가 나중에 추가될 수 있을 것이라고 생각하고 프로필 컴포넌트로 따로 분리해 느슨하게 결합했습니다. 예상대로, 프로젝트 막바지에 현재 요금제에 관련된 정보가 표시되어야 한다는 요구사항이 생겼고, 프로필 컴포넌트에 요금제 정보만 추가해서 쉽게 해결할 수 있었습니다.
프로젝트가 끝난 지금도 비즈니스 레이아웃 컴포넌트에 변동사항이 생기면 쉽게 적용할 수 있을 것 같다는 자신감이 있습니다.
sprint 4
네 번째 스프린트에서는 기업사용자가 특정 조건으로 인재를 탐색하고 제안을 보낼 수 있는 기능을 구현했습니다. 사실상 현재 서핏의 비즈니스 프로덕트 중 매출을 이끌어내기 위한 핵심 기능입니다. 이 작업물은 비즈니스 서비스 이용자분들과 많은 부분이 맞닿아있기 때문에 특히 UX/UI 적인 디테일한 부분에 신경을 썼는데요, 이 과정에서 기술적으로 깨달은 부분이 있었습니다.
상태의 흐름은 일원화되는게 좋다
조건 필터링 컴포넌트 구현 방향
깨달은 것을 이야기하기 전에 구현해야하는 기능에 대해서 자세히 살펴보겠습니다. 조건에 맞는 인재를 검색하기 위해서 직군, 직무, 경력, 가능한 제안 유형, 회사, 학교, 스킬을 조건으로 설정할 수 있는데요, 이렇게 설정한 조건은 URL의 params로 표현이 되어야 합니다. 설정한 조건이 URL의 params로 표현이 되면 특정 조건의 인재 목록을 공유할 수 있다는 장점이 생기게 되기 때문입니다.
실제 구현 결과물의 문제점
처음에 이 기능을 구현할 때 아래 사진처럼 흐름이 만들어지게끔 작업을 진행했습니다. params의 조건 데이터와 컴포넌트 내부의 조건 state를 같은 격의 데이터라고 생각했기에, 조건이 수정되면 두 데이터를 같이 수정해주는 방향으로 로직을 구현했습니다. 이렇게 플로우를 구성해도 기능 구현은 완성되니 별문제가 없다고 생각했지만, 코드 리뷰 단계에서 문제가 있다는 것을 깨달았습니다.
조건이 추가되거나 수정되기 전 형태가 바뀌어야 하는 등 필터링 조건에 대한 추가적인 수정이 있을 때, 작업을 두번씩 해줘야 한다는 문제가 있음
위 1의 문제로 인해 데이터가 멱등하지 않을 가능성이 생김
해결 방향
"검색 조건" 이라는 데이터가 일원화되어 흐름이 명확하게 구성되는 것에 주안점을 두고 아래 사진처럼 설계를 다시 진행했습니다. 복잡한 로직을 추상화된 굵직한 작업들의 조합으로 만들고, 추상화된 작업들을 단계별로 나눠서 설계하니 코드의 흐름이 훨씬 명확하게 변하는 경험을 했던 것 같습니다.
비즈니스에 임팩트를 내는 개발자?
글또 8기를 시작하면서 "비즈니스에 어떤 임팩트라도 만들어내려는 몸부림을 보여주겠다" 라고 포부를 밝힌 적이 있습니다. 실제로 프로젝트를 진행하는 내내 그런 개발자가 되려면 당장 뭘 해야 하지? 라는 물음을 계속했었는데요, 이번 프로젝트를 통해서 다양한 형태로 임팩트를 만들어낼 수 있다는 것을 알게 되었습니다.
프로덕트를 만드는 엔지니어로서
빠르게 변하는 비즈니스적인 요구사항을 큰 비용을 들이지 않고 충족시킬 수 있는 기민한 코드를 만들어낸다면, 그런 비즈니스적인 선택을 하면서 걸림돌이 없게 될 것입니다. 프로덕트를 만드는 엔지니어로서 그런 방향성으로 일을 하면 비즈니스에 임팩트를 만들어낼 수 있으리라 생각합니다.
같이 일하는 동료로서
엔지니어는 본질적으로 문제를 해결하려는 사람일 겁니다. 주변에 있는 다른 사람들이 맞닥뜨린 문제를 해결한다면 그것도 비즈니스적인 임팩트를 낸 것이라고 볼 수 있지 않을까요? 이번 프로젝트에서는 운영 프로세스에 맞게 관리자 페이지를 구축한 일이 이에 해당할 것 같습니다. 이외에도 데이터 집계, 루틴한 작업들 자동화 같은 것들도 비즈니스 임팩트를 만들어낼 수 있을 것입니다.
스타트업의 일원으로서
프로젝트를 끝내고 나서 근래에는, 엔지니어로서 해야하는 일보다는 일원으로서 할 일이 더 많았습니다.이번주에는 우리 프로덕트를 사용하고있는 기업의 HR 담당자분과 인터뷰를 진행했습니다. 우리 프로덕트를 사용하시면서 기대하셨던건 무엇인지, 꾸준히 사용하기 위해서는 어떤 부분에 개선이 필요할지, 다른 서비스를 이용하면서 느꼈던 불편한 점은 무엇인지, ... 인터뷰를 통해 시장의 반응을 확인하고 당장 해야하는 일감을 만들 수 있었습니다. 이것 말고도, 우리 프로덕트를 콜드메일로 알리기 위해 여러 기업들의 이메일 주소를 수집하는 단순 반복 작업도 일주일 내내 진행하였는데요, 이런 작업들을 하면서 무엇인가 파는 일이 무엇인가 만드는 일만큼 힘들수 있다는 것을 깨달았습니다.
이렇게 이번 프로젝트에서 얻은 것들을 회고해보았습니다. 작은 스타트업에서 일하는 엔지니어가 어떻게 일하는지 조금이나마 경험하게 되셨길 바라며 글을 끝내겠습니다. 👋