일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- error
- 회고
- CRUD w ReactQuery
- 멀티스레드
- Spa
- TypeScript
- 멀티프로세스
- 짝선배 짝후배 매칭 웹 개발 회고
- refactoring
- wanted-preonboarding-course
- CS
- teave
- Today
- Total
깊고 넓은 삽질
3개월간 진행한 4개의 팀 프로젝트 회고 몰아쓰기 본문
짧은 기간동안 다양한 사람들과 팀 프로젝트를 할 기회가 있었다. 각 프로젝트를 진행한 시간도 모두 2주 내외로 짧기도 하고 쉬는 기간도 짧아서 바로바로 회고록을 쓰지 못했다. 그래서 이 포스팅 하나에 각 프로젝트에 대한 짤막한 회고를 적고자 한다.
아래의 모든 프로젝트에서 팀장과 풀스택을 맡았다.
프로젝트의 결과물들은 현재 배포를 중단하였습니다.
시연 영상과 발표자료는 구글 드라이브 링크를 참고해주세요.
1. 침착맨 팬 페이지 만들기 프로젝트 (2주)
Typescript, React, Express, MongoDB
GitHub - calmdownmanfanpage/calmdownmanfanpage: 침착맨 팬페이지
침착맨 팬페이지. Contribute to calmdownmanfanpage/calmdownmanfanpage development by creating an account on GitHub.
github.com
기획 의도 | 마음 맞는 사람 찾기
함께 오랫동안 개발을 해 나갈 마음 맞는 사람을 찾고 싶었다. 좋아하는 게 비슷하다면 잘 맞을 것 같았다. 당시 침착맨이라는 유투버를 재밌게 보고 있어서 침착맨 팬 페이지 만들기라는 프로젝트를 기획했다. 기간이 짧기도 했고 웹 개발이 처음이신 분들도 있어서 결과물에 욕심내지 않고 가벼운 마음으로 프로젝트에 임했다.
시도 및 성과 | 남에게 관심 가지기 ➡ 인기상?
가볍고 즐겁게 프로젝트를 진행하고 싶었지만 편하고 즐거운 분위기를 만드는게 참 어려웠다. 코드 충돌이 일어나지 않게 페이지 단위로 나눠서 작업을 했는데 이러다보니 모이더라도 각자 작업을 하게 되었다. 서로의 문제를 같이 고민하는 분위기를 원했지만 현실적으로 그런식의 협업은 어려울 것 같았다.
그래서 협업보다는 팀원 간 편한 분위기를 만드는 것 자체에 집중했다. 팀원 간 소통에 대한 조언을 얻기 위해 기관의 코치님께 소프트스킬을 여쭤보기도 하고 책을 사서 읽기도 했다. 그 중 데일 카네기의 인간관계론에서 이런 말이 기억에 남는다. (기억에 의존한 것이라 내용이 정확하지 않을 수 있다..)
남에게 호감을 얻거나 남을 설득 시키는 방법은 단 하나다.
중요한 사람이 되고 싶은 욕망을 채워주는 것이다.
남을 중요한 사람처럼 대하라.
표면상이 아닌 진심으로 남을 중요하게 생각하라.
남을 진심으로 중요하게 생각하는 것도 연습과 훈련이 필요한 것 같다. 머리로 생각해보면 타인에게 감사할 일 투성이고 중요하다는 것을 알고 있지만 마음과 몸은 아직 진심으로 누군가를 중요하게 대하는게 어렵다.
목표로 했던 마음 맞는 사람도 찾았고 나름 즐겁게 프로젝트를 진행해서 만족스러웠다. 게다가 열심히 얼굴을 비추고 다녀서 그런지 운 좋게 인기상까지 받았다.
아쉬운 점과 다음에 시도할 것 | 기획 의도에 맞게 일 분배하기
페이지 단위로 따로 나눠서 개발할 게 아니라 짝코딩 형식으로 하나의 주제를 같이 고민했으면 더 좋았을 것 같다. 페이지로 나눠도 서로 겹치는 부분이 반드시 있기 때문에 서로 대화할 줄 알았지만 생각대로 되지 않았다. 이렇게 가벼운 프로젝트를 할 기회가 있다면 굳이 빡빡하게 일을 분리할 필요가 없을 것 같다. 항상 목표를 고려해서 계획을 세워야겠다.
2. 책 판매 웹 사이트 구축 프로젝트 (2주)
HTML, CSS, Javascript, Express, MongoDB
GitHub - team-daladala/daladala-bookstore: Vanilla JS로 쇼핑몰 만들기
Vanilla JS로 쇼핑몰 만들기. Contribute to team-daladala/daladala-bookstore development by creating an account on GitHub.
github.com
기획 의도 | Vanilla JS + 백엔드 입문
2주안에 쇼핑몰 MVP를 구현하는 것을 목표로 두었다. 상당히 짧은 기간이기 때문에 라이브러리나 프레임워크가 필요하다고 생각했다. 하지만 팀원 대부분이 React나 Nextjs와 같은 프레임워크나 라이브러리를 써보지 않아 쓰기가 곤란했다. Typescript도 마찬가지였다. 그래서 빠른 구현에 목표를 두지 말고 이번 기회에 HTML과 바닐라 자바스크립트로 구현해보는 걸 도전했다. 이번 팀은 기관에서 정해준 팀이었는데 백엔드 희망자가 한 명도 없어서 전공자인 내가 풀스택을 도전했다.
시도 및 성과 | 팀과 최대한 많이 소통하기 ➡ 최우수상
네 번의 프로젝트 중 가장 정신없이 진행한 프로젝트였던 것 같다. 처음엔 최대한 디테일하게 계획을 짜서 일정 안에 결과물을 내고 싶었다. 팀원 대부분이 웹 개발을 처음 접하시는 분들이었다. 그러다보니 깊게 고민해야 하는 부분과 바로 해결할 수 있는 부분을 구분하기 힘들것 같았다. 그래서 진행 상황을 자주 체크해 어디서 막히고 있는지를 파악해야 해 빠르고 잦은 소통이 필요했다. 하지만 사정상 오프라인 모임이 힘들어서 디스코드 음성 채널과 피그마, 노션 등 온라인으로 할 수 있는 모든 소통 방법을 동원해 최대한 소통을 많이 하려고 시도했다. 다행히 소통에 다들 적극적으로 임해주셔서 프로젝트 마감일 이전에 배포까지 성공했다.
HTML에서 동적 데이터들이 어떻게 올지 미리 구조화할 순 없을까
스타일링 할 때의 일이었다. 장바구니 테이블 바디에 있는 td 태그들의 스타일을 수정해야했다. 일단 HTML 파일을 켜본다. 그럼 텅빈 tbody에 아이디값만 있다. 다시 JS를 키고 해당 아이디를 사용하는 DOM을 불러오는 코드를 찾고 그 코드에서 클래스 명을 찾아 스타일링을 해야했다. 이럴 땐 template이라는 태그를 사용하면 동적으로 불러올 데이터를 HTML에 미리 표현할 수 있다. template 태그는 HTML에 렌더링되지 않고 있다가 document.importNode로 불러서 특정 DOM에 붙이면 그 때 렌더링된다.
<body>
<!-- 정의된 템플릿은 화면에 표시되지 않음 -->
<template id="myTemplate">
<p>This is a template content.</p>
</template>
<!-- JavaScript로 템플릿 내용을 가져와 사용 -->
<script>
// 템플릿 요소 가져오기
const template = document.getElementById('myTemplate');
// 새로운 인스턴스 생성
const templateInstance = document.importNode(template.content, true); // 두번째 인자는 하위노드까지 복사 여부
// 가져온 템플릿을 문서에 삽입
document.body.appendChild(templateInstance);
</script>
</body>
HTML에서 모듈화와 재사용성 추구하기 | Web Component, Shadow DOM
리액트에 익숙해진 나는 HTML에서도 리액트 컴포넌트 같이 재사용 가능하게 모듈화 되기를 원했다. 그러나 HTML을 import 해오면 html태그 전체를 불러올 수 밖에 없었다. 찾아보니 Web Component라는게 있었다. 깊게 공부하진 않았지만 Web Component는 간단히 설명하자면 커스텀 태그다. 리액트에서 <MyComponent></MyComponent>를 사용하듯 HTML에서도 <my-component></my-component> 이런 식으로 사용이 가능하다.
Web Component는 내부적으로 Shadow DOM을 사용하는데 이는 캡슐화된 DOM 트리를 사용해 css 스타일링이나 js 코드를 외부와 격리시켜준다.
장바구니 상태관리 지옥 | 어케했어요 선배님들;
장바구니 구현 시 관리 해야할 상태가 흩어져에 있다보니 상태관리가 굉장히 힘들었다. 가령 수량을 하나 늘리고 선택을 취소하면 그에 따라 상품 개별 가격과 총상품금액이 변해야 하는데 라이브러리나 프레임워크 없이 개발한다는게 얼마나 힘든 일인지 여실히 느낀 프로젝트였다.
한개의 상태(DOM의 innerText)가 바뀔 때마다 그와 연관된 모든 DOM을 업데이트 시키는 형식으로 상태 관리를 구현했다. 가령 상품 총 가격(ProductsPrice)이 변경되면 그에 따라 계산서(Bill) 전체가 리랜더링되는 방식이다. 기존에는 각각의 DOM이 서로 연관된 DOM을 직접 업데이트 시키는 다방향 데이터 흐름이었다면, set 할 때마다 연관된 DOM 전체를 리렌더링해 단방향 하향식 데이터 흐름을 구축하고자 했다.
아쉬운 점과 다음에 시도할 것 | 팀을 고려한 기획
너무 힘들었던 2주여서 아쉬운 점이 딱히 없던 것 같다. 다만 기관에서 요구한 쇼핑몰 MVP 요구사항을 뒤로하고 더 줄여서 우리 팀 나름대로의 속도로 일정을 짰다면 좀 더 재밌을 것 같긴하다. 다음 기획은 좀 널널하게 잡고 싶다는 생각 뿐이었던것 같다.
3. 운동 파트너 매칭 PWA 구축 프로젝트 (3주)
Typescript, React, React-Query, Express, MongoDB, Firebase(web push)
heal-mate
heal-mate has 2 repositories available. Follow their code on GitHub.
github.com
기획 의도 | 헬스 보조 쉽게 찾기, 빠른 MVP 구축 시도 (2트)
게시글 작성이나 세부적인 조건 설정이 귀찮은 사람들을 위해 간단하지만 필수적인 정보로 빠르게 운동 보조 파트너를 찾을 수 있게 도와주는 서비스를 기획했다. 기획이 아무리 잘 됐다고 한들 변화는 사람의 힘으로 막을 수 없다고 생각한다. 그래서 기획 범위를 최소한으로 잡고 변경에 유연한 구조를 구축하는 것을 목표로 했다.
시도 및 성과 | 작게 기획하고 코드 재사용으로 효율적으로 개발하기 ➡ 실패..
기획을 최소화 한다고 생각했지만 예상치 못한 일들이 겹치면서 일정이 늘어져 결국 배포를 하지 못하고 마무리됐다.
Firebase 도입을 시도했으나 AND 쿼리 문제
백엔드를 최대한 배제하고 프론트엔드에 집중하고 싶었다. 그래서 serverless를 알아봤고 그 중 firebase를 사용해보기로 했다. 그러나 우리 서비스가 필요한 쿼리를 firebase에서 지원하지 않는 문제가 있었다. 해당 문제를 정리해 포스팅한 링크를 남기겠다. firebase 도입 시 반드시 고려해야 할 것 | AND 쿼리 제약
결국 firebase는 web push만 쓰고 백엔드는 mongoDB를 이용해 처음부터 다시 구축했다.
재사용 집착이 초래한 복잡도
짧은 기간동안 성과를 내려면 효율적으로 일해야 한다. 효율적으로 개발하려면 반복되는 일을 없애야 한다. 한 번 만든 코드를 재사용할 수 있다면 다시 만들지 않고 그대로 쓰는게 효율적일 것이다. 그러나 재사용이 코드를 복잡하게 만든다면 오히려 효율을 떨어뜨리기도 한다. 재사용에 집착하다보니 하나의 컴포넌트에서 억지로 분기문을 만들어서 컴포넌트가 복잡해졌다. 그러다보니 개발 속도가 점점 더뎌져 일정이 또 밀렸다.
아쉬운 점과 다음에 시도할 것 | 더 작게 기획, 사람이 보기 좋은 코드 짜기
이번 프로젝트는 욕심이 좀 과했던 것 같다. MVP를 더 작게 잡을 수 있는 기획을 가져갔다면 배포까지 성공했을 것 같다는 아쉬움이 남는다. 또한 재사용에 집착하느라 정작 그것을 사용하는 개발자를 고려하지 않았다는 점이 참 민망하다. 클린코드나 원리원칙에 갇히지 말고 유연한 사고가 필요한 것 같다. 또한 관심사 분리를 하기위해 수평적(계층적)으로 폴더구조를 구축했는데 정작 일 분배는 기능 단위로 해서 폴더 이동이 잦아져 피로했다. 다음 프로젝트 때도 기능별로 일을 분배한다면 수직적 폴더구조를 고려해봐야겠다.
4. 공간 공유 SNS 웹 서비스 구축 프로젝트 (3주)
Typescript, React, React-Query, Express, MongoDB
GitHub - ggsno/spacestation: 공간 공유 웹 서비스
공간 공유 웹 서비스. Contribute to ggsno/spacestation development by creating an account on GitHub.
github.com
기획 의도 | 빠른 MVP 구축 (3트)
이전 프로젝트들로부터 메타 인지력을 키웠다.. 팀단위의 일정은 내 마음대로 되지 않기 때문에 최대한 넉넉하게 일정을 잡아야 했다. 주어진 기간이 3주밖에 없기 때문에 기획 범위를 대폭 줄였다. MVP의 미니멈을 초초미니멈으로 잡고 시작했다. 우선 기획에 들이는 시간을 더 줄였다. 3주 안에 끝낼 프로젝트에서 기획에 공을 들이기 보다는 작더라도 하나의 온전한 서비스를 만들고 싶었다. 그래서 이미 있는 서비스인 인스타그램과 오늘의 집의 컨셉을 차용해 공간 사진 공유 서비스를 기획했다.
이번 팀은 기관에서 정해준 팀이어서 역시나 백엔드가 없었다. 지난 프로젝트 때의 경험을 근거로 삼아서 이왕 이렇게 된거 모두가 풀스택으로 해보는건 어떨지 팀원들을 설득했다. 다행히 모두 동의를 해주셨다. 팀원 모두에게 기능 단위로 일을 분배하고 각자 본인이 맡은 기능에 대해 풀스택으로 구현했다.
시도 및 성과 | 코드를 보기 쉽게 만들기 ➡ 대상(!)
이전 프로젝트에서 피로를 느꼈던 부분인 분리된 맥락을 이번에는 응집도를 높혀서 해결하려고 시도했다. 기존 client의 폴더구조는 대략 이런 구조였다.
관심사를 분리하는 것 까진 좋았지만 우리에게 필요한건 수평적 분리(레이어를 분리)보다는 수직적 분리였다. 예를 들어 components 폴더 아래에 있는 게시글 컴포넌트를 개발하다가 해당 컴포넌트가 사용할 API를 보려면 근처 파일이 아닌 service 폴더로 이동해 API를 봐야했다.
그래서 이번엔 components에 기능단위로 폴더를 만들고 해당 기능과 관련된 코드들을 모두 모아뒀다. 프로젝트 피드백으로 components의 폴더 이름을 features로 하는 건 어땠을까라는 조언을 들었는데 정말 components보단 features가 더 맞는 것 같다.
이렇게 나누니 기능 단위 작업이 수월했고 바뀐 commit을 보기도 좋았다.
비동기 컴포넌트를 선언적으로 처리하기 | Error Boundary, Suspense
본 포스팅에서는 비동기 API 호출로 pending, success, error 등의 상태를 가지는 데이터를 다루는 컴포넌트를 편의상 비동기 컴포넌트라고 부르고 있습니다. 적절한 용어가 따로 있다면 댓글로 알려주시길 바랍니다.
비동기 컴포넌트를 다룰 땐 번거로운 일이 참 많았다. 랜더링될 때 데이터는 pending 상태 이므로 useEffect로 데이터를 불러와야 하고 pending 혹은 error 상태일 때도 그에 맞는 컴포넌트를 띄워줘야한다. 게다가 받은 데이터를 해당 컴포넌트 외에서 사용하거나 mutate해야 한다면 받은 데이터를 클라이언트의 데이터와 sync를 맞춰줘야한다. 효율적인 데이터 패칭을 위해서는 캐싱도 해줘야한다. 이를 모두 구현한다면 코드를 보기가 쉽지 않을 것이다.
이 모든 것을 리액트 쿼리 하나로 해결할 수 있었다. 리액트 쿼리 훅으로 비동기 상태를 선언적으로 커버할 수 있고 쿼리키로 캐싱 뿐만아니라 stale time, cache time, isFetching, isLoading 등으로 UX면에서도 세세하게 컨트롤할 수 있다.
여기에 더해 suspense query를 사용하거나 throwOnError 옵션을 true로 주면 로딩과 에러 상황을 좀 더 선언적으로 다룰 수 있다. 우리는 suspense와 error boundary를 가지는 API Boundary라는 컴포넌트를 만들고 비동기 컴포넌트에서 이를 재사용했다. 주의해야할 점은 suspense와 error boundary 모두 해당 컴포넌트 내부에서 throw하는 것은 잡지 못하기 때문에 반드시 다른 컴포넌트로 감싸야 한다. 아래는 HOC를 만들어서 각 비동기 컴포넌트에 호출한 코드이다.
참고) ErrorBoundary는 컴포넌트 라이프사이클 중 componentDidCatch를 활용해야하기 때문에 클래스로 구현해야 한다. 우리 팀은 공식 문서에서 대체제로 알려준 react-error-boundary 라이브러리를 사용해 함수형 컴포넌트로 사용했다.
판단은 모두 함께
팀원 한 분이 화면 전체를 덮는 modal 컴포넌트에 대한 custom hook을 만들어 주셨다. 그런데 해당 훅은 modal 컴포넌트에서 사용될 상태 뿐만 아니라 JSX 컴포넌트 자체를 반환하고 있었다. 처음 이 코드를 보고 뭔가 불편한 느낌이 들었다. 왜 그런가 생각해 봤는데 UI 요소에서 상태 로직을 분리하기 위해 custom hook을 사용한다고 알고 있는데, 이런 hook에 다시 UI 관련 요소를 넣어서 어색했던 것 같다.
하지만 이건 어디까지나 내 생각이었다. 내 생각을 표현하는 것 보다 다른 팀원들이 사용하는데에 불편함이 없다면 이대로 넘어가도 될 것 같았다. 제작 의도를 들어보니(지금은 기억 나지 않지만..) 일견 타당해 보였다. 친절하게도 sample 컴포넌트까지 만들어 주셔서 사용법도 남겨주셨기 때문에 별 문제가 안될 것 같아서 approve를 눌렀다. 그러나 다른 팀원이 불편하지 않았다는 건 내 착각이었다. 후에 modal을 사용할 일이 있을 때 상태와 UI가 얽힌 custom hook을 이해하고 사용하는 게 부담스러워서 본인이 따로 만들어도 되냐고 묻는 팀원도 있었다.
코드에서 냄새가 나기 시작한다면 내가 판단하지 말고 팀원에게 의견을 바로 드러내야겠다. 내 코가 잘못된건지, 코드가 잘못된건지는 팀원들이 알려줄 것이다.
백엔드도 작은 디테일 챙기기
어쩌다보니 4번의 프로젝트 모두 백엔드를 다뤘다. 서당개 3년이면 풍월을 읊는다고, 몇 번 백엔드를 구축해보니 전에 놓쳤던 기본적인 부분들이 보이기 시작했다. 가령 삭제 트랜젝션이나 에러 로그 관리같은 기본적이지만 이전엔 생각도 못했던 것들을 얼추 흉내 내봤다. 거의 블랙박스였던 백엔드 쪽도 조금씩 시야가 트이는 느낌이라서 뿌듯하다.
진짜 받을 줄 몰랐는데...
작은 사이즈의 기획과 코드를 보기 좋게 관리하기, 그리고 팀원들의 피 땀 눈물이 합쳐서 결국 마지막 프로젝트는 대상을 받았다!
+ ) 부끄럼이 많아서..
심리학부생 시절 MBTI 검사를 한 적이 있다. 그 땐 MBTI가 지금처럼 유명하지 않았던 때였는데 학부 공부를 하면서 여러 검사들을 받아 보면서 겸사겸사 봤던 것 같다. 분류해 놓은 기준으로 각 수치가 몇 대 몇으로 결과가 나오는데 나는 내향 점수가 만점이었고 외향 점수가 0점 이었다. 성향도 그랬지만 외향적 능력도 부족했었다. 사람들 앞에 서면 머리가 하얘지고 얼굴은 붉어졌었다.
컴퓨터 공학을 복수전공하면서 팀 과제를 몇 번 했는데 같은 문제를 함께 밤을 새며 고민하는게 너무 재밌었다. 그 때에도 피치못할 상황이 아니면 내가 먼저 다가가지 못했다. 하지만 컴공 팀과제는 타인과 함께 있어야할 피치못할 상황을 많이 만들어줘서 관계의 즐거움을 알게해줬다. 주변에 좋은 사람이 참 많았던 것 같은데 좀 더 적극적으로 인간관계를 맺지 않은 것이 아쉽다.
이번 과정에 들어가면서 내 목표는 나와 마음 맞는 사람을 찾는 것이었다. 마음 맞는 사람을 찾기 위해 부지런히 내 마음을 드러내고 다른 사람들의 마음을 들여다보려고 노력했다. 상대는 모르겠지만 나 혼자 상처받기도 하고 나도 모르게 상처를 주기도 했을 것이다. 다만 이런 과정으로 생기는 관계의 기쁨이 고통보다 큰 것 같다. 앞으로도 계속 나와 타인의 마음을 부지런히 듣고 더 나은 사람이 되고싶다.
함께해 준 팀원들에게 참 고맙고 열심히 할 동기를 불어넣어 준 다른 팀들의 동기들에게도 감사하다. 모두 같은 길을 걷다가 다시 만나면 참 반가울 것 같다. 이번 과정에서 받은 리더쉽 상 수상 사진으로 이번 회고를 마치고자 한다. 내향인들 화이팅이다.
++) 데뷔햇습니다.
열심히 얼굴을 비추고 다니니 이런 기회도 있었다. 사회성 결여된 AI 신봉자 역할로 나온 것 같지만..