마지막 포스팅 날짜가 언제인지 보니 작년 7월 1일이다. 저 맘때만 해도 블로그 업로딩을 이렇게 까지 쉴 줄은 몰랐었다. 뭐, 꼭 블로그 업로딩을 주기적으로 해야한다는 필요성은 없지만 이 정도 까지 늦춰질 줄이야... 핑계라면 핑계고 바쁘다면 바쁜 시간 속에 지금에서야 반년 만의 글을 써본다.
2023년 나의 개발적 일지를 회고식으로 작성해 볼 까 하였지만 타이밍도 늦은감이 없지 않아 있고, 가장 큰 이유는 좀 오글거려서이다. 아무튼 그렇다.
본격적인 글을 작성하기에 앞서 조금 뜬금 없지만 나의 velog를 잊은 시기에 생각보다 방문자가 많이 늘었다. 몇 개의 글 들엔 질문형식의 댓글도 달려있었고 그에 답변을 하는 것 또한 색다른 재밌거리였다. 방문자가 어느정도 쌓이는 대부분의 포스팅은 나의 메인스택인 Typescript와 NestJS에 관한 글이였고 조금씩 조금씩 NestJS란 프레임워크를 사용하는 분들이 늘고 있다는 생각도 해본다.
NestJS를 처음 시작하였을 때, (물론 나의 서칭 능력의 이슈일 수도 있지만...) 생각보다 국내 개발자 분들이 작성하신 글들이 많지 않았고 더더욱 나와 같은 초보자가 읽었을 때 "이 프레임워크는 도대체 어떤 느낌일까...?" 를 인지시켜주는 글은 찾기 힘들었다. 그래서 그 당시 무슨 생각이었는지 모르겠지만 나조차도 NestJS를 모르면서 괜히 나와 같은 입문자를 위한 "성지"가 될 수 있는 NestJS 블로그를 만들어야겠다고 생각하였다. 그리고 휴식기를 거친 지금부터 다시 이어나가고자 한다.
오래간만에 글을 쓰다보니 주저리주저리 서두가 길었고, 그럼 이제 몇 개의 포스팅으로 시리즈가 만들어질지 아직은 나조차도 가늠이 안되는 몇 개월 간의 기록을 남겨보고자 한다.
o2o
서비스블로그 포스팅이 멈춰 진 해당 시점, 우연한 기회로 인연이 닫게 된 대학 창업팀의 IT 개발자분과 대화하는 시간을 가졌다. 비전공자이며 독학으로 서버 개발을 공부해오던 나로썬 처음 대면으로 누군가와 개발에 관해 얘기를 나눈 시간이였고 오가는 대화 속에 "프로젝트" 참여에 점점 가까워지고 있었다. 그 당시를 생각해보면 팀 프로젝트를 참여하는 것에 끌리는 것도 있었지만 단순 아는 사람들끼리 모여서 하는 프로젝트라기 보단, 아직은 작지만 "투자"를 받은 창업 사업단 이란 것에 조금 더 끌림이 있었다. 평소 스타트업이나 서비스 기획에 있어서도 관심이 많았던 나로썬 개발자로서든, 혹은 사회 초년생으로서든 한 단계 시야를 넓힐 수 있는 기회가 될 것임엔 분명하였다.
(회사의 세부적 내용, 제휴를 맺은 매장의 규모, 투자 단계및 방향성 등에 관한 내용은 생략하도록 하겠습니다)
그렇게 난 "식음료 온라인 주문 결제 서비스" 미리에 NestJS 서버 개발로 합류하게 되었다.
o2o
서비스와 스마트 오더 (서비스 소개)진행중인(아직은 마무리가 아니므로...) 플랫폼 서비스는 "o2o 서비스", 조금 더 명확하겐 "스마트 오더 서비스"이다. 유저가 앱을 통해 특정 매장의 식음료 제품을 결제-주문 한 뒤, 매장에서 해당 제품을 수령하는 서비스이다.
아마 이 글을 읽으시는 분들은 바로 알아차리시겠지만, 이 서비스엔 유저와 마주하게 되는 "앱"이 존재해야 할 것이고, 주문을 접수하게 되는 매장의 "포스기(웹)"가 존재해야 할 것이다.
이에 따라 개발 팀은 "앱 클라이언트 - 앱 서버", "웹 클라이언트 - 웹 서버"로 분리해 진행하게 되었다 (+추가로 어드민 페이지 또한 존재한다).
이 중 난 "앱 서버 개발" 파트를 맡게 되었고, NestJS 프레임워크로 진행하게 되었다.
지금에서야 옅은 미소라도 띌 수 있지만 처음 프로젝트를 맡을 당시엔 어느정도 무서움도 존재하였다. 실제 유저가 사용하게 될 서비스, 결제가 진행되는 서비스 ... 이런 점을 다 떠나서 초기 안으로 구축된 와이어프레임(db, 피그마 등...)을 보았을 때 이걸 내가 만드는게 가능한가에 대한 의문이었다. (조금 tmi로 얘기하자면 배포까진 아니더라도 투자처에서 요구하는 시연 테스트 마감 기한 이 존재하였다)
앱 서버 개발을 나와 다른 한 분 이렇게 2명이 참여하게 되었지만 인원 수의 부족으로(여러 사정으로 인원을 모집하는 것에 제약이 있었다) 사실상 초기 멤버로 와이프레임 및 디비 설계를 담당하신 앱 서버 파트너 분 께선 디비 설계 및 구축에 시간을 더 많이 할애할 수 밖에 없었다.
이에 따라 앱 서버 개발에 대한 베이스 구축 및 전반적 API 개발을 담당하게 되었고, 구현해야 할 기능은 대표적으로 아래와 같았다. (정말 눈에 보이는 기능에 대해서만 기술하자면 아래와 같다)
회원가입 및 인증 처리를 통한 로그인 프로세스 구현
위치 정보 설정 및 탐색에 따른 조회
무한스크롤을 구현하기 위한 페이징 api 설계및 구현
매장 및 메뉴 등 유저에게 제공해야 할 정보에 대한 조회 api 구현
장바구니 설계부터 pg사 연동을 통한 결제 시스템 도입 및 주문 프로세스 api 구현
웹 서버와의 서버 to 서버 소통을 통한 주문 접수/취소/물품 수령 기능 구현
FCM(Firebase Cloud Messaging)을 활용한 푸시 알림 서비스 기능 구현
리뷰 생성/수정/삭제 기능 구현
....
....
포괄적으로 구현해야 할 기능을 정의한다면(API 중심) 위와 같이 말할 수 있을 거 같다. 하지만 이 글을 보시고 계시는 서버 개발자 분들은 아시겠지만... 위의 기능 구현 하나하나에 더 많은 세부적 장치들이 가지를 뻗으며 필수적으로 요하게 된다. 이에 대해선 아래에서 더 자세히 언급하도록 하겠다.
다음 내용에서 조금은 더 "개발적" 접근에서 얘기를 하겠지만, 기획적 측면에서 접근하더라도 실제 운영될 목적으로 설계되고 만들어져야 할 서비스에선 api 설계 그 이상으로 중요시 생각되어야 할 여러 이슈 및 프로세스들이 존재하기 마련이다. 지금에서야 "존재하기 마련이다." 라고 하였지만 규모 있는 프로젝트에 처음 도전하는 나로써는 그 당시 이 모든 것들이 미지와의 만남이었다.
무엇을 얘기하는 것일까?
간단한 예시를 들어보자면, 결제-주문 프로세스를 구축한다고 하자.
"pg사 연동해서 결제 처리 맡기고, 주문 로직은 트랜잭션을 통해 일관성 보장해주고... 그러면 되는 것 아니야?"
그렇다. 위와 같은 식으로 처리될 것이다. "하지만" 이것이 전부는 아니고, 전부가 되어선 절대 안된다. "기능 구현"에 목적을 두어선 안되는 상황이기 때문이다.
계좌 간의 이체 등을 다루는 금융 서비스 까지는 아니더라도 우리의 앱 서비스를 통해 엄연히 "금융 거래"가 이루어진다. 돈이 오고가는 상황에서 발생하는 문제는 유저에게든 서비스 제공자에게든 불편한 상황 (규모가 클 수록 최악의 상황...)으로 이어지게 된다.
이에 따라 실주문 이전, 쿠폰및 포인트 그리고 최종 금액의 데이터 일치성을 체킹하기 위한 "가주문" 생성이 선수될 필요가 있었다.
더하여, 메인 결제-주문 프로세스 하나를 좋은 설계로 잘 구축하였다고 해서 이것 하나만을 믿기엔 불안하지 않을까 생각하였다. 어떤 이유로든지 분명히 메인 프로세스가 터질 수 있다고 생각을 하였고, 결제는 이루어졌지만 주문이 들어가지 않은 상황에 대한 체킹을 할 필요가 있었다. 하지만 이는 클라이언트와의 소통으로 이루어지는 것이 아니므로 별도의 "주문서 누락 체킹 배치 프로세서"란 안전장치를 요하게 되었다.
내가 이 프로젝트 유지 보수를 할지, 이 앱 서비스에 어느 정도의 유저가 모일지 이 모든 것은 미지수이지만 그러한 상황과, 더하여 나의 능력과는 관계없이 "엔지니어"로써 이는 당연히 취해야 할 제스처라고 생각하였고 지금도 변함이 없다.
(거의 다 완성이 되어가는 이 시점에서)프로젝트를 진행하며 프로그래밍 외적으로 나에게 생각보다 큰 메시지로 다가왔던 것은 "서비스 개발을 위한 각 팀의 조화로운 균형과 진정성"이었다.
앞서 언급하였지만 아무래도 학생 창업단 규모의 프로젝트이다 보니 비용의 한계에 따른 문제들이 많았다. (이는 어쩔 수 없는 문제였다) 기획, 디자이너, 개발자가 "하나"가 되어 이 앱에 몰두하는 것이 사실 상 힘들었다. 자체 서비스에 대한 기획은 정해져있었지만 "앱(App) API"에 대한 기능 명세 정의, 사용할 서드 파티(pg사, 소셜 api 등)에 대한 세부적 명세, 그리고 명확한 화면 정의서가 부족하였다.
이에 따라 자연스럽게 개발을 진행하며 애플리케이션 기능 정의에 대한 기획을 동시에 진행하게 되었고 코드를 작성하는 개발 외적으로 신경써야할 부분들이 항상 선수되었다. 비슷한 서비스를 제공하는 다른 o2o (스마트 오더) 커머스 플랫폼을 벤치마킹하는 시간을 가져보았으며 여러 기업 사내 블로그를 엄청 많이 뒤졌던거 같다.
서버 개발에 온전히 집중하지 못하였다? 이 뜻으로 말한 것은 전혀 아니다. 오히려 좋은 경험이었다고 본다. 나의 첫 회사가 어떤 도메인 될 지는 모르겠지만 뭐가 되었건 다가올 도메인에 익숙해지는데 오늘의 경험이 큰 도움이 될거라 생각한다.
아무튼, 말하고 싶었던 건 "기획", "디자인", "클라이언트 및 서버 단 개발" 등 이러한 모든 과정들이 좋은 방향으로 조화를 이루어 나아갈 때 비로소 "하나"의 괜찮은 서비스가 만들어진다고 확신적으로 느끼게 되었다.
위에선 조금은 서비스 자체에 대한 고찰및 환경적 요소에 대해 얘기를 나눠보았다면 지금부턴 온전히 "서버 개발자"로서의 얘기를 풀어보고자 한다.
(세세한 기술적 내용은 다음 포스팅부터 순차적으로 진행할 것이므로 깊게는 들어가지 않겠습니다)
NestJS
는 그저 도구다.따지고 보면 더 중요한 것은 typescript, javascript가 될 수도 있겠지만 서버 전체 프로젝트의 근간이 되는 NestJS 프레임워크가 엔지니어링 단계에 있어서 메인 포인트라 할 수 있었다. 글의 서두에 언급하였다시피 여태 독학으로만 NestJS를 공부하였던 나에게 하나의 완성형 서비스를 구축하란 것은 두려움으로 다가온 건 사실이다. 물론, 유데미에서 NestJS 강의를 하나 듣긴 하였지만 인도영어 강의에다가 (값이 싸서 그런지...) 기능 구현 그 이상의 아무런 정보를 제공해주지 않았던 터라 사실상 정글에 던져진 채로 공부하고 있었다. (원래 그런 타입이기도 하다...)
그러면서 자연스래 NestJS를 공부하며 경험한 기능적 문제나 느낀 생각들에 대해 블로그에 기술하게 되었고 (단순한 문법적 설명은 블로깅하지 않는다) 이러한 작지만 의미있던 시간이 이번 프로젝트에 대한 두려움을 오로지 두려움만으로만 그치게 했던거 같다.
소제목에서 언급하였지만 프로젝트를 진행한 처음 시점과 달리 지금에서야 느끼는 생각은 NestJS는 그저 프레임워크일 뿐이였다. 구현해야할 기능마다, 혹은 서비스마다 상이하겠지만 나에게 있어 NestJS는 "전부"라기보단 "이용"하는 것에 가까웠다.
그렇다고 해서 NestJS란 프레임워크에 대해 깊이 알 필요없다? 이런 뜻은 절대로 아니다. "클라이언트의 요청 - 서버내 로직 수행 - 응답"으로 이어지는 주기 내에서 NestJS의 라이프사이클은 어떻게 진행되는가를 명확히 알필요가 있었고, 이에 따라 미들웨어의 책임을 도와주는 Enhancer(guard, pipe, interceptor, filter ...)들과 메인 로직들의 역할 분리를 이루어낼 수 있었다. 더불어, 내가 만약 커스텀한 무언가를 만들어내고 싶다면, 그리고 그것이 NestJS 혹은 내부 라이브러리의 source code를 마개조해야할 상황이 일어난다면 이 역시 프레임워크의 내부를 들여다 볼 필요가 충분히 존재하였다.
하지만, 이렇게 "NestJS"란 프레임워크 자체가 제공해주는 기능들, 그리고 라이프사이클에 대한 이해도 및 인지만 깔려있다면 "핵심"은 NestJS 자체가 전혀 아니었다.
"백엔드" 자체에 대한 이해도가 더 중요하지 않았나 싶다. 점점 구현해야할 기능이 커지고 도메인(물론 도메인의 단위는 작을지언정...)이 늘어날수록 오히려 NestJS에서 제시하는 기본적 Dependency Injection과 @Injectable()
데코레이터를 통한 계층간의 의존 결합도가 걸림돌이 되기도 하였다. (아키텍처 관련된 자세한 내용은 다음 포스팅에서 다룰 예정입니다)
Enhancer들을 제외한 비즈니스 로직에선 NestJS 의존도를 벗어나는(모듈에게 전부 위임) 설계를 진행하게 되었고 결국 중요한 것은 클라이언트와 마주하게 되는 프리젠테이션 단부터 데이터를 저장-관리하는 영속성 영역까지 어떻게 데이터를 매끄럽게 "잘" 전달할 것인가에 대한 고민이었다. 이는 프레임워크 종속적인 얘기가 아닌, 서버 아키텍처및 프로그래밍 관점 지향 전반에 걸친 고민이다. 실제로 프로젝트를 진행하면서 (물론 NestJS 관련글이 적은것도 사실이지만) Nest로 짜여진 코드의 글보다 (Java or Kotlin)Spring으로 소개된 글들을 훨씬 더 많이 참고하였다.
결국 프로젝트를 맞는 시점에서 나는 "코더(Coder)"라기보단 "엔지니어"에 가까워야한다 생각하였고, 언어나 프레임워크는 생산을 위해 활용하는 도구로써 충분하였다.
API를 설계하는데 있어서 어느 부분에 가장 많은 시간과 두뇌를 사용했는가?
orm을 통한 crud 쿼리문, 그리고 코드 한 줄, 한 줄 물론 중요한 것임에 분명하지만 사실 이건 어쩌면 서버 개발자 만의 온전한 책임이고 좋은 플로우만 만들어졌다면 문제없이 손가락 노동을 통해 진행될 수 있다.
그렇다, 중요한 것은 API 설계를 위한 "클라이언트 to 서버"간의 "플로우(flow)" 설계였다.
앱은 유저가 직접적으로 마주하게 되는 서비스이다. "유저 경험(UX)"은 클라이언트와 조금은 더 가까운 얘기일 수 있겠지만 (pm이 따로 존재하지 않는 기획에선) 서버 개발자역시 UX에 민감해야한다 판단하였고 지금도 그렇게 생각한다. 특히나 우리의 도메인에선 더더욱 그럴 필요가 있었다.
⨀ 가게 상세 페이지 - 메뉴 상세 페이지 - 장바구니 - 결제서 - 결제 - 주문 ⨀ 으로 이어지는 화면(UI)에서 유저가 불편함을 겪게 되는 상황을 항상 떠올리고 가정해야했고, 유저의 편안한 사용 경험을 위해 API의 응답을 수정 및 추가하는 작업은 필수적이였다. (모든 API가 원큐에 해결된 적이 단 한번도 존재하지 않았다.)
이러한 과정속에 클라이언트 분과 자주 소통하며 유저의 경험을 증진시키기 위한 API 플로우를 짜는 것에 많은 시간을 투자하였고, 이러한 과정이 가장 힘들면서도 동시에 가장 의미있었지 않았나 싶다. (결국 유즈케이스는 전체 개발팀이 함께 관여를 해야하는 것이 아닐까...)
추후 개별파트로 포스팅 하겠지만 성공및 실패 응답에 대한 통일화 및 구체화, 그리고 스웨거(Swagger)를 통한 문서화 이러한 작업에 힘을 더 많이 쓰기도 하였다.
개발을 진행하면서 내 머릿속에서 가장 많은 충돌을 불러일으켜 왔고, 선뜻 손가락을 움직이게 할 수 없었던 부분이었다.
아래의 코멘트들을 살펴보자.
"비지니스 로직에 있어서는 프레임워크및 라이브러리에 종속적이지 않은 설계를 하자."
"계층간의 역할과 책임을 분리하고 디커플링 지향의 아키텍쳐를 구성하자"
"공통된 로직을 항상 재사용성 있게 분리하도록 하자."
... ...
난 완벽하게 도메인으로부터 라이브러리및 프레임워크 종속성을 벗겨내었는가?
내가 작성한 아키텍쳐의 각 계층은 철저히 분리된 각자의 역할이 존재하고 이는 모든 도메인에서 공통적인가? 그리고 이는 개발 전반적으로 걸친 유명한 아키텍쳐의 설계와 유사하게 돌아가고 있는가?
도메인마다 공통적으로 사용될 함수에 대해 재사용성 있는 분리를 항상 추구하였는가?
... ...
결론은 "아니다"
그럼 난 실패한 설계를 한 것일까?
설령 누군가가 "너의 아키텍쳐와 전반적 설계는 실패했어. 좋지 않은 사례야" 할 지언정 나 스스로는 "아니"라고 말 할 것 같다.
정말로 클린 아키텍처란 무엇일까.
물론 어딘가에 소속되어 개발을 진행하고 있지만 난 엄연한 취준생이며 아직은 프레시한 상태이다.
서비스 개발을 진행하는 사람은 "나"이지, 클린 아키텍처 개론을 저술한 저자가 아니다. "나"의 개발적 능력은 스스로가 가장 잘 알고, 클린 아키텍쳐 개론이 추구하는 어쩌면 이상향적일수 있는 코드를 현 능력으론 구현이 힘들다는 것 또한 인지하고 있다.
이러한 상황에서 "클린 아키텍쳐"는 쫒아야 할 무언가가 아니라 유연하게 참고하며 받아들여야 할 방법론이라 생각이 든다. 정말 언젠가는 클린한 코드를 완벽하게 내 머릿속에 저장하고 받아들여 구현할 수 있다면 얼마나 좋을까? 하지만, 상황에 따라 중요한 것은 그것이 아닐지도 모른다는 것이다.
유저에게 일련의 서비스를 제공해야한다는 측면에선, 그리고 정해진 기한내에 어떠한 목표치를 구현해내야한다는 측면에선 "기능 구현, 성능, 등등 ..." 어쩌면 더 우선순위로 고려해야 할 사항들이 많다.
추구하고자 하는, 혹은 팀이 기준으로 정해놓은 "틀"에서 조금은 예외가 발생하였다고 해서 거기에 "실패"를 들이밀 필요는 없다고 본다. 결국 개발을 진행하는 것은 "팀 멤버"들이고 팀의 능력치와 비용을 고려한 설계가 최선의 설계지 않을까 싶다.
시발점치곤 꽤 흥미로웠고 나의 인사이트도 더욱 증가하게 된 시간이었다. 아직 서버 간 통신, 그리고 배포까지 조금의 프로세스가 남았지만 전반적 개발을 마무리하는 이 시점에서 기능을 구현했던 그 당시의 모든 모습들이 하나하나 전부 떠오르는 것 같다.
함께 개발을 진행하였던 열정적인 팀원분들께 고마움을 전하는 것은 물론이거니와, 아무것도 몰랐던 내가 개발자의 형태라도 낼 수 있게 많은 도움을 주신 "개발바닥 2사로" 선생님들께 고마움을 전달하고 싶다. 실무자분들께 직접 듣는 배움은 단순 개념적 설명을 넘어 현실적인 문제에 맞딱들였을때에 굉장히 큰 도움으로 다가왔다.
그럼 긴 글 읽어주셔서 너무 감사드리고... 다음 글 부턴 개발 전반적인 내용을 다뤄보도록 하겠습니다.
이상...