-
[회고록] 프로젝트 시작 후 3개월, 나는 그동안 무엇을 했을까 (PocketMark v1.0)개인회고록 2022. 3. 3. 00:14
12월초에 팀 프로젝트 PocketMark 를 시작하여 3개월의 시간이 흘렀다.
경험하고 싶었던 나 자신과의 싸움(레거시 리팩토링)은 실컷 경험할 수 있어서 좋았지만 이때 쯤이면 솔직히 계획했던 메인서비스와 기본적인 부가서비스들이 모두 구현완료되어 있을 줄 알았다.
자동차는 아니더라도 자전거 정도는 만들어질 줄 알았었는데 대신 킥보드가 만들어져 있었다.
뭐... 일단 굴러가기는 하니 다행이라고 해야하나...
팀원 한분이 취직으로 그만 두셨지만 나는 앞으로도 계속 사이드프로젝트로 두고 키워나갈 예정이다.
3개월간 내가 한 일은 크게 두 가지이다.
첫번째는 사용자 도메인(회원, 로그인, 인증)을 제외 한 모든
도메인의 설계와 서비스 로직을 구현하고 개선
하는 일.두번째는 반복적으로 해야만했던
노가다성 작업들의 자동화
이다.그리고 백엔드 팀의 추가목표는 프로젝트를
TDD
로 만들어가는 것이었다.
1. 메인서비스를 구현하고 개선
우리의 서비스가 굴러가기 위한 최소한의 필요조건인 폴더/북마크 저장기능을 구현해야 했다.
1-1 리팩토링 비용을 감소시키자!
나의 핵심 관심사였다.
내가 이 프로젝트에 손을 떼고, 다른사람이 계속 프로젝트를 발전시켜나가야 한다면
과연 나의 코드때문에 뒷사람이 고통 받지 않을 것인가?일단 컴퓨터 시스템의 폴더와 파일처럼 구현해야 했고, 어떻게 효과적으로 설계해서 유지보수 비용을 줄일 수 있는가에 초점을 맞추었다.
항상 그렇듯이 일단 스케치코드부터 구현하여 API를 정상작동 시킨 후, 여러 레퍼런스들을 보며 개선했다.
사실 리팩토링을 진행하면서 API 명세서를 3번정도 수정했었는데,
나 때문에 프론트분도 덩달아 API호출부분을 3번정도 수정하셔야 됬었다. (죄송합니다..)
이러한 과정을 겪으며 API 스펙변경에 의존적인 코드는 나쁜코드라는 것을 깨닫게 되었으며,
이후부터는 의존성 탐지견이 되어 양방향 의존성이 생기지 않도록 신경쓰고 있다.다행히도 이제는 API 스펙이 프론트의 요청없이는 변경이 안되게끔 관리하고 있다.
> API->Repository 의존성 제거하여 코드재사용성이 높도록 설계하기
> 나는 게으르다... 하나로 여러가지가 됬으면 좋겠다... (@Inheritance, Interface-Based Projection)
그래서 결과는?
엔티티 스펙이 변경되거나, API 스펙이 변경될 때 확실하게 고쳐야할 코드 수가 줄었다. 의존성을 줄이자 캠페인을 진행하면서 레이어를 넘나들때 마다 DTO를 따로 두었는데 확실히 유지보수의 이점을 느끼고 있다. 다만 컨트롤러레이어와 서비스레이어 사이의 DTO는 아직 이렇다 할 편리함을 못느끼고 있다. 하지만 DTO를 없앴다가 나중에 새로 생성해야되는 상황이온다면 비용이 너무 클 것 같아 일단 유지하는 중이다.
1-2 서버부하를 감소시키자!
배포까지 생각하고 만들었기에 서버부하 감소도 주요 관심사 중 하나였다.
북마크 저장 서비스는 가벼운 I/O가 많이 생길 것이 자명하기에 어떻게 이 비용을 줄일 수 있을까 고민하다가 생각해낸 것이 브라우저(React)에서 일정시간(5분으로 협의)마다 데이터의 CUD 기록을 쌓아뒀다 서버로 전송하는것이었다.
지금와서 돌이켜보면, 참 바보같은 제안에 호응해주시고 그걸 선뜻 구현해주시겠다고 한 프론트팀원분께 진심으로 감사드립니다.
단건 요청이 아닌 다량의 요청을 처리하는 컨트롤러와 서비스를 구현해본 것은 좋은 경험이었다.
> 컬렉션 리소스에 맞게 URL 구성하기> 데이터 C/U/D 일괄로 처리하기그러나 이 방법은 브라우저에게 책임을 돌리고 무겁게 만들뿐만 아니라, 프론트의 코드 생산성을 저하시킬 우려가 있다.
그리고 한 명의 유저가 작업한 데이터를 일괄처리 하는 것이기 때문에, 그 유저가 5분안에 500건 이상의 Request를 생성하는건 굉장히 드문일이다. 따라서 클라이언트 사이드 캐싱으로 얻을 수 있는 이점은 실질적으로 딱히 없다.
(죄송합니다 프론트팀원님... 제가 부족해서 고생만 하셨네요... ㅠㅠ...)
추후에는 서버사이드캐싱으로 서버에서 책임을 지도록 구성할 예정이다. (Redis, Global Cache 를 기억해두자.)
> 더불어 불필요한 쿼리가 생성되지 않도록 쿼리 튜닝에 힘썼다.
그래서 결과는?
MockMvc테스트 기준 xx건 이상에서 차이를 보인다. Spring에서 작성해 둔 Batch 처리는 추후 서버사이드캐싱을 적용할 때 도움이 될 수 있겠으나, 5분마다 Request 보내기는 실질적으로 서버비용 감소에 기여된 건 없어 아직 개선해야 할 과제로 남아있다.
> 성능 테스트 결과반면 JOIN과 WHERE절의 인덱스 성능, 트랜잭션 오버헤드, 쿼리 갯수 줄이기등에 신경쓴 것은 확실히 서버부하 감소에 기여했다고 생각한다.
더불어 한국 레퍼런스에서 거의 언급되지 않는 Spring Data Interfaced-Based DTO Projection 을 찾아내 코드생산성과 성능 두마리 토끼를 잡은 것은 스스로에게 칭찬해줄만 하다.
2. 반복적인 업무 자동화
3개월간 프로젝트를 진행하면서 가장 힘들고 귀찮았던 시간은
프론트분께 공유할 API 명세서 작성이었다. (진심)
물론 Swagger라는 라이브러리를 알고는 있었지만, 서버를 계속 실행시켜두어야 한다는 점과 코드변경마다 다시 또 실행해주어야 한다는 점 때문에 Notion에 손으로 직접 API 명세서를 작성하고 공유하고 있었다.
그리고 이제 메인서비스 구현이 끝났고, 본격적으로 부가서비스(공유게시판, 친구추가, SNS공유, 추천시스템, 통계등)를 구현해야하는데 기능을 구현할 때마다 API 명세서를 작성해야하는 것 때문에 너무나 숨이 막혔다.
(문서작업보단 개발이 하고싶어....요)
많은 기능의 추가를 눈 앞에 둔 상태에서 기존의 개발환경을 유지한다면 프로젝트 진행이 계속 더뎌질 것 같아 자동화 환경을 구축하기로 마음 먹었다.
2-1 API 명세서 생성을 자동화하자! (CI/CD 환경구축)
코드변경마다 감지해 변경된 코드를 빌드하고 실행시켜주는 무언가가 필요했다. 그리고 빌드된 코드를 계속 실행하고 있을 컴퓨터가 필요했다.
이왕이면 Git에 연동된 플러그인 같은게 없을까 싶어 찾아보다 Travis CI 라는 녀석을 발견했다.
(Jenkins, AWS CodeBuild도 있다)
그리고 빌드된 코드를 계속 실행시키기 위해서
(내 컴퓨터는 소중하니까)남의 컴퓨터(AWS)를 사용하기로 했다.그렇게 결국 CI/CD 환경을 만들어내고, 이제 API 문서를 노가다처럼 작성할 필요가 없어졌다. (감격... x100)
그래서 결과는?
Swagger 사용으로 API 문서를 힘들게 작성할 필요가 없게 되었다.
> https://back.pocketmark.site/swagger-ui
2-2 자동화환경을 구축하다 보니 외부에 배포된 우리의 서비스
일단 내부적으로 사용하려는 목적이었는데 프로젝트 베타버전이 완성되면 어차피 배포해야하니까
하는 김에 도메인을 구입해서 이쁜주소로 배포까지 해버렸다 ☆
모야모야 이거 되게 뭔가 뿌듯해.
데이터 분석가로 일할 때, iptime으로 사내 인트라넷에 배포하여 외부에 서비스 한 경험은 있지만
이렇게 이쁜 도메인으로 배포를 한적은 처음이다...
심지어 서버컴퓨터를 계속 가동하지 않아도 되니 너무 감동이었다...
그런데 HTTP 인게 너무 보기 싫었다.
"우리사이트는 가장 기초적인 보안처리조차 안되어있어요 :D!!! HAHAHA"
라고 외치는 것만 같았다.
HTTPS의 SEO 기능도 탐이나지만, (아직 개발경력이 없는 나의 영역은 아닌것 같고)
가장 기초적인 보안처리는 해주고 싶었다.
>> CloudFront, EC2 SSL 적용기그래서 결과는?
외부에서 우리서비스를 HTTPS로 사용할 수 있다!!!
반성의 시간
1. 명확하게 일하자
계획보다 속도가 더디었던 이유는 무엇일까?
웹개발에 미숙했던 나를 포함해 개개인에게도 물론 책임이 있겠지만, 가장 큰 이유는 리더십의 부재라고 생각한다.
누군가는 서로 공부하며 진행하고 싶은 것과 프로젝트의 완성을 위해 진행해야하는 것을 분리하고 책임을 나누었어야 했는데, 아무도 나서지 않고 추상적으로 역할을 나누었다.
각자의 역할을 나누기는 했는데 그 역할들이 서로에게 너무 의존적으로 나누어진 것도 한 몫 했다. 서로가 미숙해 어쩔 수 없었던 일이었지만 다음에는 더 잘하고 싶은 마음이다.
앞으로는 상세한 역할 분리와 데드라인 설정등으로 팀원들과 명확하게 회의를 진행하려 한다.
2. TDD
당초 100% TDD가 목표였지만, 이제 막 배워나가는 입장에서 BDD와 같은 개념이 섞이면서 혼동이 와서 테스트 코드 작성을 중간에 중지하였다.
실제로 돌아가게끔 테스트코드를 짜는게 맞는거 같은데, 구글링을 통해 예시코드를 들여다보면 thenReturn() 같은 결과를 정해놓은 테스트코드들이 있어 많이 혼란스러웠다.
Mocking 처리도 많이 혼란스러웠다. Mocking 처리와 thenReturn() 같은 것을 사용하면 코드리팩토링이 어떻게 되든 항상 테스트코드가 통과하는데 그게 과연 테스트 코드의 의미에 부합하는가 의문이 풀리지 않았다.
물론 Mocking 처리를 하여 필요한 부분만 로드한다면 테스트코드 실행속도가 빨라져서 빌드속도가 빨라지겠지만 이를 위해 코드의 안정성을 버려야 하는가 의문이 생겼다.
다행이도 최근에 지인분이 ATDD를 수강하시길래 이것저것 궁금한 것을 여쭈어보았다.
내가 가진 의문사항에서 개발자들의 의견이 많이 나뉜다고 한다. 제일 좋은 방법은 안정성을 유지하면서 확실히 동작함을 인지하는 코드만 Mocking 처리나 thenReturn()으로 처리로 코드생산성과 빌드속도를 가져가라고 조언해주셔서 의문이 해소되었다.
앞으로는 프로젝트를 계속 진행하면서 비어있는 테스트코드를 채워나가려고 한다.
앞으로 해야 할 일
테스트코드 작성과 부가서비스들의 기능을 구현 해야한다.
또 앞서 언급한 서버사이드 캐싱도 적용해야하고 기회가 된다면 Spring Batch도 한번 다루어 보고 싶다.
거의 제로베이스에서 3개월간 정말 열심히 해서 나름 뿌듯하지만 뒤돌아보면
더 나은선택들이 있었음을 깨닫고 더 채찍질하게 된다.
동시에 재미도 많이 느껴 데이터분석가에서 백엔드개발자로 전향하길 너무 잘했다는 생각이 든다.
728x90