Skip to main content
← 블로그

프로젝트 담당자 기능을 만들다

VauDium ·

서버는 이미 준비되어 있었습니다. UI를 붙이고, 검색 화면을 만들고, 빠진 데이터를 채우기까지.

프로젝트 담당자 기능을 만들다

Fecit에는 프로젝트 기능이 있습니다. 여러 사람이 하나의 프로젝트에 참여하고, 태스크를 공유할 수 있습니다. 그런데 “이 태스크는 누가 하는 거야?”를 알 수 있는 방법이 없었습니다.

담당자(Assignee) 기능을 만들기로 했습니다.

서버는 이미 준비되어 있었다

코드를 살펴보니 서버에는 이미 assigned_to 필드가 있었습니다. API 엔드포인트도 있었고, 권한 체크도 구현되어 있었습니다. 누군가 예전에 기초 작업을 해 둔 겁니다.

데이터 모델도 갖춰져 있었습니다. 태스크에 assigned_to로 담당자의 ID를 저장하고, 프로젝트 멤버인지 검증합니다. 담당자가 바뀌면 이전 담당자의 동기화에서 삭제 처리까지 됩니다.

없었던 건 UI뿐이었습니다.

어디에 넣을까

태스크 상세 화면에서 프로젝트 바로 아래에 “담당자” 행을 넣었습니다. 탭하면 검색 화면이 열립니다.

처음에는 BottomSheet 모달로 멤버 목록을 보여줬는데, 멤버가 많으면 검색이 필요하겠다는 생각이 들었습니다. 주소 자동완성에서 썼던 패턴 — 별도 검색 화면으로 이동하는 방식 — 을 그대로 적용했습니다.

검색 화면에는 프로젝트 멤버가 나옵니다. 닉네임으로 검색할 수 있고, 본인에게는 작은 “나” 배지가 붙습니다. 멤버를 탭하면 담당자가 설정되고 이전 화면으로 돌아갑니다.

이름을 어떻게 보여줄까

담당자의 이름을 표시해야 합니다. 서버에서 태스크를 내려줄 때 assigned_to에는 ID만 들어 있습니다. 이름을 알려면 별도로 사용자 정보를 가져와야 합니다.

처음에는 클라이언트에서 프로젝트 멤버 목록을 가져와서 ID로 매칭했습니다. 동작은 했지만, 태스크를 열 때마다 멤버 목록 API를 호출하는 건 비효율적이었습니다.

서버에서 태스크 응답에 담당자 정보를 포함시키기로 했습니다. assignee라는 필드를 추가하고, 태스크를 반환할 때 담당자의 닉네임과 ID를 함께 내려줍니다. 한 번의 요청으로 필요한 정보가 다 옵니다.

보이다가 안 보이는 문제

구현을 끝내고 테스트하는데, 담당자가 보이다가 안 보이는 현상이 발생했습니다. 새로고침하면 다시 나타납니다.

원인을 추적해 보니, 개별 API(담당자 설정, 태스크 조회)에서는 assignee 정보가 포함되지만, 리스트 조회(gather)와 동기화(sync)에서는 빠져 있었습니다.

서버에서 태스크를 반환하는 경로가 두 가지였습니다. 개별 API는 enrich라는 과정을 거치면서 프로젝트 정보, 담당자 정보 등을 채워 넣습니다. 하지만 리스트와 동기화는 성능을 위해 별도의 배치 병합을 합니다. 프로젝트 정보는 배치 병합에 포함되어 있었지만, 담당자는 새로 추가한 거라 빠져 있었습니다.

배치 병합에 담당자도 추가했습니다. 담당자 ID들을 모아서 한 번에 조회하고, 결과를 각 태스크에 매칭합니다. 프로젝트 정보 병합과 같은 패턴입니다.

기본 담당자

태스크를 생성할 때 프로젝트를 설정하면, 생성한 사람이 자동으로 담당자가 됩니다. 기존 태스크에 프로젝트를 나중에 설정해도 마찬가지입니다. 프로젝트를 해제하면 담당자도 함께 해제됩니다.

별도 태스크 생성 화면에서도 담당자를 선택할 수 있습니다. 프로젝트를 선택하면 담당자 행이 나타나고, 기본값은 본인이지만 다른 멤버를 선택할 수도 있습니다.

데스크톱에도

모바일에서 동작을 확인한 후, 데스크톱에도 같은 기능을 추가했습니다. 데스크톱에서는 드롭다운 방식으로 — 프로젝트 섹션 아래에 담당자 행이 있고, 클릭하면 멤버 목록 드롭다운이 나타납니다.

돌아보면

서버 인프라가 이미 있었기 때문에 하루 만에 완성할 수 있었습니다. API 엔드포인트, 권한 체크, 데이터 모델 — 기초가 갖춰져 있으면 UI를 붙이는 건 빠릅니다.

다만 “동작한다”와 “제대로 동작한다”의 차이는 있었습니다. 이름 표시를 위해 서버 응답 구조를 바꿔야 했고, 리스트와 동기화에서 데이터가 빠지는 문제를 잡아야 했습니다. 기능이 단순해 보여도, 데이터가 흐르는 모든 경로를 확인해야 합니다.