1. pinia와 typescript의 도입
이번 프로젝트(커머스)에서 드디어 벼르고 벼르어왔던 기술을 도입하게 되었다. 바로 typescript
와 pinia
이다.
원래는 더 빨리 도입하고 싶었으나, 이전 프로젝트가 생각보다 늘어짐 + 숙련도 이슈로 인하여 늦어진 감이 있다. 하지만 그만큼 더 철저하게 준비해서 도입 안하느니만 못한 상황이 벌어지지 않게끔 신경썼다. 여기서 '도입 안하느니만 못한 상황'이란 기껏 타입스크립트 도입해놓고 모든 타입을 any
로 정의한다든가, 에러 해결을 못해서 js
로 회귀한다든가 하는 상황이다. 도입 초반부에는 이런 일이 실제로 벌어질 위기가 몇 번 있었으나, 그때마다 구글링하고 공식문서 열심히 읽다보니 어찌어찌 해결은 할 수 있었다.
도입해놓고 나서 느낀 점은 확실히 타입스크립트가 개발경험을 엄청나게 개선시켜준다. VScode에서 제공해주는 자동완성 기능을 맛보면 헤어나올 수가 없다. 특히나 내가 진행한 커머스는 그 특성상 프론트에서 다뤄야할 데이터가 많다. 하나의 상품 데이터 하나에도 그 속성이 20개가 넘고, 이 상품을 주문 로직을 태우려면 또 다시 주문을 위한 데이터 20여개가 추가된다.
기존 js
로 개발을 했다면 40개가 넘는 데이터 중에서 하나라도 오타가 나거나 타입이 어긋나면 직접 시연해보며 그 에러를 찾는데 시간이 잡아먹혔겠지만, ts
를 사용함으로써 런타임 이전 시점에 어디서 문제가 생기는지 쉽게 찾을 수 있게 되었다.
왜 사람들이 ts
를 제대로 쓸 줄 알기 시작하면 쓰기 이전으로 돌아갈 수 없다고 말하는지 깨달았다. 아직 내가 '제대로 쓴다'라고 말하기엔 어설프긴 하다만, ts
학습 허들의 고비점은 넘긴 느낌이다.
또 다른 기술 도입은 pinia
이다. pinia
는 vue 커뮤니티에서 출시한 vuex
를 대신할 새로운 전역 상태관리 라이브러리이다. 사실 vuex
를 대신한다고는 해도, 그 개발진 대부분이 옮겨간 수준이라 크게 달라진 점은 없다.
기존 vuex
를 사용하면서 느꼈던 불편한 점들, 그리고 ts
와의 호환성 증대로 인하여 서로의 시너지를 극대화시킬 수 있다는 장점이 있다.
pinia
에 대한 도입기는 이전 포스팅에 자세히 적어놓았다.
2. 페이지 / 컴포넌트 구조에 대한 고민
개발을 시작한지 얼마 되지 않은 초창기의 개발 스타일은 가급적이면 한 페이지에 내에 많은 기능을 압축적으로 집어넣으려고 했다. 개발이 익숙하지 않을 시기에는 파일을 여러 개로 쪼개는 것에 대한 부담감(두려움)도 있고, 상대적으로 규모가 작은 프로젝트를 맡았었기에 '페이지,컴포넌트 구조론'에 대해 고민이 깊지는 않았다.
하지만 프로젝트의 규모가 점점 커질수록 페이지,컴포넌트의 개수가 많아지게 되고, 여러 파일을 다루는 일도 익숙해지면 자연스레 이 파일들을 어떤 기준으로 나눠야 효율적일지 고민하게 되었다.
일단 페이지란 사용자가 서비스에서 제공하는 특정한 기능 한 가지를 수행하기 위해 만들어진다. 커머스 서비스로 예를 들자면 상품 전시, 결제, 장바구니, 주문조회와 같은 기능에는 모두 고유한 페이지가 존재한다. 그리고 페이지는 각자마다 고유한 path(url)
를 부여받는다. 이 path
가 사용자와 개발자간의 접점이 되어서 상호작용이 가능한 것이다.
컴포넌트는 그 페이지를 구성하고 있는 일종의 부속품이다. 컴포넌트에는 버튼이나 헤더,푸터, input창 하나하나도 개발자의 판단에 따라서 컴포넌트가 될 수도 있다. 컴포넌트를 분리하는 기준은 저마다 다를 수 있지만 나같은 경우에는 프로젝트 내에서 반복적으로 사용되는 여부와 기능을 구성하는 최소한의 단위를 따져서 만들게 된다.
즉 하나의 페이지 안에도 수십개의 컴포넌트가 존재할 수 있고, 이 말은 곧 작은 기능을 담당하는 부품(컴포넌트)이 여러개가 합쳐짐에 따라 기능을 수행할 수 있게 되는 것이다.
그래서 내가 페이지를 나누는 기준은 사용자의 행동 단위를 큼직하게 잘라서 구성하는데, 이 기준이 칼로 두부자르듯이 항상 말끔하게 구분되지는 않고 애매모호한 지점이 생긴다. 때로는 특정 행동에서 파생된 기능을 다뤄야 할 때가 있다. 예를 들어 '주문조회' 페이지에서 '배송조회'라는 파생기능이나, '상품 주문'페이지에서 '주소 검색'같은 기능들이다. 이러한 파생기능을 구현하려면 둘 중 하나의 방식을 선택해야 한다.
1. 메인 페이지에 녹여내기
가장 간단한 방법은 한 페이지에 두 기능을 다 표시하면 된다. 하지만 2가지의 별도의 행동을 하나의 페이지 안에 모두 보여주기에는 부담이 있다. 위에서 언급했던 페이지의 정의와 같이 하나의 페이지 안에서는 한 가지 기능만 수행하도록 하는 원칙에 위배되기 때문이다. 파생된 기능을 욱여넣음으로써 그 페이지만의 맥락을 모호하게 만들고 싶지는 않았다.
2. 별도의 페이지(path
)로 만들기
1번의 방법이 싫다면 페이지 자체를 분리하면 된다. 하지만 페이지를 남발하게 되면 그 만큼 path
의 수가 많아지게 된다. 한 페이지 내에서 파생 기능이 10가지라면 새로운 path
10가지가 추가된다는 뜻인데 과연 이게 효율적인 방법인지는 고민해봐야 한다.
무엇보다 페이지를 쪼개면 메인 페이지 => 파생 페이지 로 데이터를 전달하기가 난처해진다. 페이지 => 컴포넌트 구조에서는 props
를 통해 전달할 수 있지만, 페이지 => 페이지 구조에서는 props
전달이 불가능하다. 따라서 상태관리의 힘을 빌린다거나, 파생 페이지에서 다시 새로운 데이터를 fetch
해야하는 비효율적인 문제가 발생한다.
결국 방법 1,2 중 완벽한 방법은 없다. 하나의 페이지라는 제한적인 공간(맥락)안에서도 여러가지 파생된 기능을 넣어야 한다. 나는 이를 해결하기 위해서 모달을 사용하였다. 모달은 별도의 path
를 부여하지 않더라도 공간을 분리시킬 수 있다. 평소에는 메인페이지를 보여주고 있다가 사용자가 파생 기능을 선택했을 때에만 모달이 기존 화면을 덮음으로써 공간을 분리할 수 있다.
그래서 결론을 얘기하자면 이번 프로젝트에서는 모달을 적극적으로 사용하였다. 모달은 '페이지와 컴포넌트 사이 고민'의 산물이다. 나는 페이지는 가능한 적게 만들면서 컴포넌트는 최대한 쪼갤 수 있을만큼 쪼개는 방식을 선호하다보니 이런 고민을 하게 된 것 같다.
3. 셀프 코드리뷰(클린 코드)
셀프 코드리뷰라는 말이 이상하게 들릴 수 있다. 하지만 회사에 프론트 개발자가 나 혼자밖에 없으니 누군가에게 코드 리뷰를 부탁하기 어려운 상황이다. 그래서 나는 회사에 다니는 동안 '좋은 코드'란 무엇일까에 대해 고민을 많이 할 수밖에 없었다. 그리고 이번 프로젝트에서 주안점으로 둔 3가지는,
- 시간이 지난 뒤에 다른 사람이 읽어도 자연스럽게 읽히는 코드
내가 짤 때는 나름 잘 짠 것 같은데, 나중에 다른사람이 내 코드를 읽고서 읽기 어렵다는 피드백을 들은 적이 있었다. 내가 보기에 잘 짠 것 같은 코드는 아무 의미가 없다. 모두가 이해할만한 보편적인 코드를 짜야한다. 특히 변수명과 함수명을 잘 짓는 일이 중요하다고 생각한다. 이름이 좀 길어지더라도 해당 변수(함수)가 하는 역할이 이름만 보고도 짐작할 수 있어야 한다.
- 버그가 발생했을 때, 어느 부분을 고쳐야 하는지 직관적으로 찾을 수 있는 코드
버그가 발생하지 않는 방향으로 코드를 작성하는 게 가장 바람직하겠지만, 코드를 작성하다보면 버그 발생은 불가피하다. 그래서 버그가 발생하지 않도록 하는 일만큼 중요한 게 '버그를 얼마나 쉽게 고칠 수 있느냐'이다.
대개 해결하기 어려운 버그들의 공통점은 하나의 코드 안에서 여러 로직들이 이어폰줄처럼 엉켜있을 때 발생한다. 어떤 부분을 고치면 이곳에 의존하고 있던 또 다른 함수에게 사이드이펙트가 전파되어서 또 다른 버그가 발생하고 만다.
- 한 번에 한 가지만 신경쓰는 코드
세 번째 정의가 어쩌면 첫 번째, 두 번째 조건을 모두 만족시키기 위한 대전제일 수 있다.
단일책임원칙이라는 객체 지향 프로그래밍의 원칙 중 하나이기도 한데, 하나의 객체는 딱 한 가지의 역할만 담당해야 한다는 원칙이다. 프론트엔드 코드가 엄밀한 객체지향 코드라고 하긴 어렵지만, 이 원칙 하나만큼은 신경써서 개발하는 게 도움이 된다.
그 점 때문인지 이번 프로젝트는 이전 프로젝트보다 파일의 개수,디렉토리의 개수, 함수의 개수 등이 월등히 많아지게 되었다. 이전 프로젝트에서는 귀찮다거나, 코드의 양을 줄이는 게 무조건적인 미학이라 생각해서 하나의 함수 안에서 여러가지 일을 동시에 처리하는 경우가 많았었는데, 이번에는 그런 로직은 철저히 배제하였다.
4. 효율적인 소통 방식
마지막은 개발 외적인 이야기이다. 첫 번재 프로젝트 회고에서도 언급했다시피 프로젝트에서 가장 어려운 문제는 개발이 아니라 사람과 사람간의 소통의 문제인 경우가 더 많다. 특히나 프론트엔드 개발자는 그 특성상 기획/디자인/백엔드 모든 분야에 다리를 걸치고 있다보니 소통의 중요성이 더욱 강조될 수밖에 없다.
이 중요성을 간과하고 무지성 개발만 하다보면 참사가 일어나기도 하는데 예를 들어 일주일 동안 열심히 개발/디자인 해놓았는데 나중에 보니 필요없는 기능이었다던지, 동료에게 특정 기능 하나를 요청했는데 나중에 보니 내 요청사항을 이해하지 못해서 기대와 다른 결과물을 받기도 한다. 그래서 이번 프로젝트에서는 내가 어떻게해야 조금 더 효율적으로 일할 수 있을 지에 대해서 먼저 고민해보았다.
첫 번째는 기획자와의 소통이다.
내가 기획자에게 가장 많이 물어본 질문은 아마 '나중에 이 기능이 추가될 수도 있나요?'일 것이다. 개발 초기에 특정 기능을 배제하고 만들어놓았는데, 프로젝트 중간에 기획자가 '이 기능 추가해주세요'하는 사태가 벌어지면 상당히 곤란해진다.
그래서 기획자와 개발자 사이에 서로의 상황을 이해하고 있어야 한다. 개발자는 기획자가 어떤 기능을 넣고 싶어하는지 파악해야하고, 반대로 개발자는 기획자에게 어떤 기능은 넣을 수 있고, 어떤 기능은 넣기 어려운지 분명하게 알려야 한다. 각자의 상황을 잘 알고 있어야만 나중에 이거 빼기로 했잖아요
,이거 넣기로 했잖아요
사태를 방지할 수 있다.
두 번째는 디자이너와의 소통이다.
이전 프로젝트에서는 대부분 디자이너가 디자인 작업과 퍼블리싱 작업까지 도맡아서 한 뒤에, 나는 퍼블리싱의 최종 결과물 위에서 프론트엔드 개발을 시작했었다. 이 방식의 경우 디자이너와 프론트 개발자의 역할 부담이 완전히 나뉘어져 있는 방식인데, 이 경우 내가 프론트 개발을 하다가 디자이너에게 돌아가서 다시 물어봐야 하거나, 아니면 퍼블리싱된 코드가 개발하기에는 적합하지 않아 코드를 지우고 처음부터 다시 짜야하는 경우가 있었다.
따라서 이번 프로젝트에서는 기초적인 디자인 작업과 퍼블리싱 단계에서부터 같이 참여했다. 기본적인 페이지 구조부터 컴포넌트를 어떤 단위로 쪼갤 것인지부터 고민했다. 그리고 자주 사용될만한 컴포넌트는 퍼블리싱 단계부터 미리 공통화를 시켜놓고 props
값에 따라서 다르게 동작하도록 하였다.
이렇게 작업한 결과, 이전보다 프론트엔드를 개발하면서 느꼈었던 모호함이 사라지고 1달이라는 비교적 빠른 시일 내에 서비스의 프로토타입을 개발할 수 있게 되었다.
세 번째는 백엔드 개발자와의 소통이다.
예전에는 프론트에 어떤 API가 필요한 상황이면 백엔드 개발자에게 두루뭉실하게 '이러이러한 기능하는 API 만들어주세요'라고 전달했었다. 구체적으로 말하지 않더라도 '무슨 말 하는지 이해하겠지?'라는 마인드여서 그랬었는데 이는 역시 나의 착각이다.
백엔드 개발자는 내가 무엇을 필요로 하는지 정확히 이해하기 힘들다. 반대의 경우도 마찬가지이다. 그래서 요청사항을 전달하는 방식을 바꿨다. 일단 '프론트에 이런 기능이 필요한데'로 시작하여 상대방에게 내가 해결하고자 하는 문제상황을 납득시켜야한다. 그 다음에 '프론트에서 이 엔드포인트로 요청을 보내면, 반환값 형식 이렇게 해서 보내주세요' 라고 전달한다.
여기서 중요한 점은 반환값 형식은 내가 우선적으로 제안을 해야한다. 백엔드에서 다루는 데이터 형식이 프론트의 그것과 상당 부분 다르기 때문에, 내가 원치 않은 형식으로 받게 될 수도 있기 때문이다.
그리고 상품별로, 혹은 주문별로 상태코드를 정의해야 했다. 백엔드는 물론이고 프론트에서도 상태에 따라서 화면에 보여줘야 할 데이터가 다르기 때문이다. 이 과정 또한 백엔드와 같이 상의하면서 '어떤 상태에서는 어떤 데이터를 보내줘야 하는지'에 대한 일종의 규약을 만들었다.
총평
나느 매 프로젝트를 진행하면서 스스로에게 미션을 부여한다. 처음 프로젝트를 맡았을 때는 vue2
에서 vue3
로 마이그레이션 하는 미션을, 이번 프로젝트에서는 pinia
와 typescript
를 도입하는 미션이었다.
이런 식으로 조금씩 내가 도전해볼만한, 개척의 여지를 찾아나감으로써 스스로 레벨업하고 있다는 느낌을 찾고 싶어서다. 누가 시켜서 하는 게 아니다. 그냥 기존에 쓰던 기술스택 그대로 써도 아무 문제 없다. 오히려 당장 개발할 때는 익숙할테니 버그도 덜 발생하고, 개발 기간도 더 짧을 수도 있다. 하지만 장기적인 관점에서, 그리고 유지보수의 관점에서는 기술을 도입함으로써 얻을 수 있는 이점이 훨씬 더 크다고 믿고 있다.
다음 프로젝트에는 꼭 Next.js
나 Nuxt.js
를 이용해서 SSR을 도입하고 싶다. 얼마 전에 살짝 찍먹해봤는데 쉽지 않을 것 같다.서버에 대한 이해도가 어느정도 갖춰져 있어야 할 것으로 보인다.