appleseed.dev

2021 상반기 회고록

더 잘하는 개발자가 되기 위해 남기는 글

June 20, 2021 · 9분
log

모든 것이 불확실하고 자신감이 없던 2020년을 보내고 맞이한 2021년, 여러 회사의 면접을 보는 중에 기대도 하지 않았던, 나의 최종 목표와도 같았던 회사에 합격을 해버렸다. 프론트엔드 개발자로서 성장하기에 더없이 최고인 자리라고 확신했지만, 그만큼 내 스스로에게 내가 요구하는 역량이 높아졌기에 과연 내가 따라갈 수 있을까? 이 정도 퍼포먼스로 내가 팀에 기여하고 있다고 할 수 있을까? 와 같은 질문을 끊임없이 되풀이했던 것 같고, 오히려 너무 일에 몰두하다가 번아웃으로 생산성이 곤두박질친 때도 있었다. 하지만 동료들에게 신뢰를 얻었다는 것을 느끼고 내 역량에 자신감을 가질 수 있게 되었고, 정말 열심히 구른 덕에 내 인생의 어느 때보다 많이 배우고 많이 성장한 3개월을 보냈다고 생각한다. 더 잘하는 개발자가 되기 위해 내가 상반기에 어떤 것들을 배웠고, 장기적으로 어떤 목표들을 가지고 나아갈 것인지에 대해 회고해보려고 한다.

As a Frontend Developer

프론트엔드 개발자의 역할은 타 개발 직군에 비해 제한적이라는 인식이 여전히 지배적인 것 같다. 하지만 기술 중심적인 기업일수록 웹을 더욱 집중적으로 활용하려고 하는 것 같고, 내 회사 또한 그렇다. 웹이 가진 크로스 플랫폼 UI 런타임으로써의 장점, 앱 업데이트 없이 즉시 배포가 가능하다는 장점 등은 그대로 가져가면서 여러 확장 기능을 통해 마치 네이티브 앱을 사용하는 것 같은 경험을 제공할 수 있음을 배웠다.

Frontend Infrastructure

내가 맡은 작업이 우연히도 AWS 인프라를 아주 세세히 다뤄야 했기 때문에 자연스레 회사에서 사용하고 있는 프론트엔드 인프라에 대해 자세히 연구하게 되었고, 원래도 관심이 많았던 분야인지라 아주 재미있게 공부할 수 있었다. 인프라를 코드로 관리하는 IaC 툴인 Terraform을 사용하여 특정 AWS 인프라 구조를 코드로 관리할 수 있고, 더 나아가 인프라에 변경사항이 생기면 마치 서비스를 배포하듯이 테라폼 코드에 커밋하고 푸쉬 후 CI를 통해 새 인프라를 일괄 적용할 수 있다는 점이 혁신적이었다.

모노레포 안에 여러 서비스들을 모아놓은 마이크로서비스 구조도 처음 접했는데, 각 서비스의 코드, 빌드, 배포 등을 모두 독립적으로 수행할 수 있다는 점에서 여러 장점을 체감했다. 예를 들면 특정 서비스가 다운되어도 나머지 서비스들에는 전혀 영향이 없다든지, 각 서비스별로 필요한 패키지들을 독립적으로 설치하기 때문에 번들 사이즈 관리 등에서도 여러 이점이 있었다. 더 나아가 모노레포의 배포 CI가 돌 때마다 새 버전이 바로 라이브되는 것이 아니라, 마이크로서비스 별로 버전 해쉬가 담긴 서비스 번들이 AWS에 올라가고 개발자가 지정한 해쉬의 버전이 Lambda@Edge를 통해 지정되어 사용자에게 보여지는 구조가 정말 멋지다고 생각했다. 예를 들어 사용자가 /some-service 경로로 요청하면 Lambda@Edge는 현재 버전 정보를 참조하여 /a1b2c3d4/some-service의 서비스를 보여주는 식이다. 배포 시에 버전 해쉬만 적절히 바꿔주면 되기 때문에 약 1초도 되지 않는 시간 안에 배포와 롤백을 할 수 있게 되었고, 이 기능이 생산성을 아주 크게 향상시켰다고 생각한다.

전사적인 인프라 외에도 비즈니스 요구사항에 따라 인프라를 만지면서 배우는 것도 있었다. 여러 도메인에서 사용되는 첨부파일 기능을 개발할 일이 있었는데, 처음에는 기본 CloudFront 설정에 따라 캐싱을 세팅했는데 기묘하게도 한 도메인에서 요청 후 다른 도메인에서 해당 파일을 똑같이 요청하면 CORS 오류가 뜨는 버그가 있었다. Curl이나 다른 HTTP 클라이언트로도 재현이 되지 않아서 한참 고생하다가 이 오류가 CloudFront cache hit이 일어날 때마다 발생한다는 것을 확인하고 헤더를 까보니 access-control-* 헤더들이 최초 요청 후에 캐싱되어서 돌아오기 때문이라는 것을 알아냈다. a.com에서 file.txt를 요청하고 나면 이제 b.com에서 같은 파일을 요청하더라도 access-control-allow-origin: a.com으로 내려오고 있기 때문에 브라우저가 CORS 오류를 띄우고 있던 것이었다. 결국 cache key에 origin 헤더를 포함하고 나니 도메인별로 헤더가 캐싱되어 오류가 해결되었다. 이 오류가 내가 지금껏 개발을 하다 만난 오류들 중 가장 난해했던 것 같다.

TODO

인프라 배포를 위해 테라폼을 사용하긴 했지만 실제로 테라폼 코드에 대한 이해는 거의 없이 기계적으로 실행했던 순간이 훨씬 많은 것 같았다. 내가 지향하는 인프라를 직접 작성하고 모듈화하여 테라폼 코드로만 표현할 수 있는 단계까지 끌어올리는 것이 목표이다.

Yarn Berry

이번에는 NodeJS 개발환경과 관련된 이야기를 하려고 한다. npm의 끔찍한 속도를 극복하고자 시작된 패키지 매니저인 yarn이 새 버전에서 완전히 새로운 방식으로 환골탈태하여 yarn v2 (berry)라는 이름으로 등장했다. Berry를 사용하면 아예 새로운 버전의 JS runtime을 사용하고 있는 것이 아닐까라는 생각이 들 정도로 개발 환경이 완전히 바뀜을 체감한다. 일단, node_modules 디렉토리가 사라지고, 모든 프로젝트의 의존성이 코드에 커밋된다. 그리고 실제로 node가 의존성을 찾을 때는 .pnp.cjs 파일을 통해 찾게 되는데, 기존의 node_modules 내의 파일 시스템을 순회하는 방식에 비해 훨씬 빠를 뿐만 아니라 안전하다. node_modules 내의 같은 이름, 다른 버전의 패키지들을 참조하는 방식은 상당히 위험하고 error-prone하기 때문이다. 덕분에 최초 코드를 clone/pull하는 시간은 기존에 비해 상당히 오래 걸리지만, 이후 yarn의 integrity를 체크하는 시간이 비약적으로 줄어들고, 의존성과 관련된 거의 모든 문제가 깔끔하게 해결된다. 자세한 건 이 포스트를 참조하자.

pnp의 사용, node_modules의 제거 외에도 yarn berry는 생산성을 비약적으로 높여주는 여러 도구들을 제공한다. 플러그인들을 이용한 각종 추가기능은 개발, 빌드, 배포 시 기존의 불편했던 점들을 시원하게 해소해준다. 모노레포 내의 마이크로서비스, 마이크로패키지들을 관리하는 workspace 플러그인, TS 타입 의존성을 자동으로 추가해주는 typescript 플러그인도 아주 유용하지만, 챕터에서 개발한 플러그인인 since는 기존의 lerna 등을 이용한 모노레포의 배포 시스템을 yarn berry 레포에 맞게 사용할 수 있도록 만든 플러그인인데, 특정 커밋과 비교하여 현재 커밋에서 변경된 마이크로서비스, 마이크로패키지들만 리스트업하거나 배포 스크립트를 실행하도록 할 수 있다. 결국 나도 기존의 블로그와 홈페이지를 yarn berry 기반의 모노레포로 통합하고, 마이크로서비스로 묶어 since로 배포하는 방식을 채택하였다. 언젠가는 테라폼 스크립트도 자동화하여 인프라의 변경사항까지 모노레포에 담아내는 것이 목표이다.

React

대부분의 시간을 react 개발에 쏟아붓고 각종 코드들을 리뷰, 리팩토링하다 보니 react를 사용하는 숙련도 또한 크게 향상되었다는 것을 느낀다. React 팀이 부어주는 인사이트와 이를 적극적으로 연구 및 도입하려는 챕터 멤버들의 열정 덕분에 나 또한 매달 더 좋은 코드를 작성하고 좋은 코드에 대해 고찰할 수 있었다.

Handling Async Jobs Declaratively

면접 당시 redux + redux-observable 조합이 선언적 비동기 작업 관리에 좋다고 자신있게 얘기했던 나 자신이 부끄러워질 정도로 비동기 작업 처리에 대한 개념이 많이 바뀌었다. 컴포넌트의 마운트 시 effect로 fetch action을 트리거하여 loading, data 등을 모두 특정 reducer에 담아두고, selector를 통해 가져오는 패턴을 당연하게 사용하고 있었는데, 컴포넌트와 데이터 간의 의존성 관계가 effect → action → epic → reducer → selector의 머나먼 경로에 따라 흩뿌려지고, 데이터를 global scope인 store에 뿌리고 다시 가져온다는 점에서 코드의 응집도, 결합도 모든 측면에서 좋지 않다는 것을 느꼈다.

hook의 사용이 일반화됨에 따라 각 데이터를 fetch해오고 컴포넌트에 부어주는 역할까지 통합한 여러 라이브러리들이 생겨났는데, 대표적으로 swrreact-query가 있다. 특정 데이터를 부어주는 커스텀 훅을 이 라이브러리들을 이용해 작성하면 컴포넌트와 데이터 간의 의존성이 명확히 드러나게 될 뿐만 아니라 변경의 이유를 커스텀 훅 하나에 한정할 수 있으므로 매우 선언적으로 비동기 작업들을 관리할 수 있게 된다.

Concurrent Features & Suspense

이러한 선언적 비동기 작업 핸들링의 흐름에 더욱 부채질을 하고 있는 기능이 react의 concurrent features이다. React v17이 나오기 전부터 실험중이던 API가 드디어 알파 테스트 기간에 접어들었는데, 하나 하나 프론트엔드 개발의 게임 체인저가 될 기능들이지만 현재 가장 주목해야 할 것은 suspense이다. Suspense로 상위 컴포넌트를 감싸고, 그 컴포넌트 트리 안의 컴포넌트가 비동기 작업의 완료를 대기하고 있다면, suspense는 알아서 주어진 fallback element를 보여준 후 비동기 작업이 완료됨에 따라 fulfill된 컴포넌트 트리를 보여준다.

Suspense의 진가는 이렇게 구구절절히 설명하기 보다 위의 swr, react-query의 suspense와의 콤비네이션을 직접 확인하는 것이 확실하다. 아래 두 코드를 보면 비동기 데이터 훅이 많으면 많을수록, 그리고 로딩 상태의 컴포넌트들이 트리에 많이 중첩되어 있을수록 suspense가 얼마나 코드를 선언적으로 만들어주는지 체감할 수 있을 것이다. 두 번째 코드에서 Child는 자신이 비동기 리소스를 사용하고 있다는 사실을 아예 몰라도 Root에서 적절히 처리된다는 사실에 주목하자.

// w/o suspense
function useUser() {
  return useSWR('/api/user').data; // data: User | undefined
}

function Root() {
  return <Child />;
}

function Child() {
  const user = useUser();
  if (user === undefined) {
    return <Loading />;
  }
  return (
    <>
      <Profile url={user.profileUrl}>
      <Name text={user.name}>
    </>
  )
}
// w/ suspense
function useUser() {
  return useSWR('/api/user', { suspense: true }).data; // data: User
}

function Root() {
  return (
    <Suspense fallback={<Loading />}>
      <Child />
    </Suspense>
  );
}

function Child() {
  const user = useUser();
  return (
    <>
      <Profile url={user.profileUrl}>
      <Name text={user.name}>
    </>
  )
}

이 외에도 등장할 useTransition 등의 기능은 앞으로 프론트엔드에서의 비동기 처리에 대한 표준을 제시할 수 있을 정도로 강력하다고 생각한다. 매년 새로운 혁신과 영감을 불어넣어주는 react 코어 팀이 대단하고 감사할 따름이다.

Good Code

비즈니스 요구사항에 쫒기다보면 간과하기 쉬운 좋은 코드를 작성하는 방법에 대해서도 많은 고민을 할 수 있었다. 챕터 내에서 오브젝트라는 책을 가지고 좋은 코드에 대해 스터디를 진행했는데, 책 자체가 객체지향 프로그래밍과 Java를 이용한 시스템 설계에 치중되어 있어 바로 실무적인 insight를 얻기에는 힘들었다. 그래도 책을 통해 가장 크게 배운 점이라면, 나쁜 코드가 왜 나쁘고, 어떻게 해야 좋은 코드로 작성할 수 있는지 지적하는 방법을 배웠다는 것이다. 소프트웨어 설계의 기본적인 원칙들만 염두에 두고 코딩을 해도 훨씬 유연하고 깔끔한 설계를 할 수 있음을 느꼈고, 지금까지 작성했던 코드들이 어째서 유지보수되지 못한 채로 버려지게 되는지에 대해서도 생각할 수 있었다.

TODO

React 개발자로서 좀 더 배우고 성장하고 싶은 부분이 있다면, 테스팅과 TDD를 도입해보고 싶다는 생각이 든다. 프론트엔드에서 테스팅이 얼마나 의미를 가질지는 여전히 논란의 여지가 있다고 생각하지만, 기본적인 API 응답들에 대해 보이는 화면이 깨지지 않을 거라는 정도는 테스트를 통해 검증할 수 있으면 편하겠다고 생각한다. QA를 거의 또는 전혀 못하는 상황에서 자동화된 테스트가 훨씬 더 위력적이라고 생각하기 때문에, 조금 시간이 더 걸리더라도 TDD를 통해 개발한다면 내가 저질렀던 실수들 중 대부분은 일어나지 않았을 것이라고 생각한다.

시간이 된다면 react core의 구조에 대해 deep dive하는 시간을 가지고 싶다. 학계에서도 상당히 앞서가는 개념들인 대수적 효과와 비슷한 기능을 react는 그냥 마법처럼 제공하는데, 언젠가 이러한 개념들이 일반화될 시대를 대비해서라도 이렇게 마법처럼 제공되는 기능들에 어떤 기술적 고민이 숨어있는지를 직접 탐구해보고 싶다.

Design System

이전에도 디자인 시스템의 중요성은 많이 느끼고 있었지만, 디자인 시스템을 아주 집중적으로 사용하며 작업하다보니 디자이너와 프론트엔드 개발자의 커뮤니케이션 코스트가 거의 0에 수렴하고 서로의 생산성이 비약적으로 높아지는 등 생각했던 것보다 전사적인 업무 역량에 끼치는 임팩트가 막대했다.

TODO

최근 우리의 디자인 시스템도 중대한 분기점에 들어서면서 새로운 구현체, 빌드 및 배포 시스템 등에 대한 요구가 커지고 있고, 나도 이 개선에 major contributor로 참여해보고 싶다고 생각했다. 떠오르는 CSS-in-JS 라이브러리인 stitches의 도입이라든지, yarn berry와 since를 사용한 모노레포 기반 빌드, 배포 시스템의 구축 등 여러 챌린징한 미션들이 남아있다. 또 한 분기가 지나고 나면 우리의 디자인 시스템이 어떤 궤도에 올라서 있을지가 기대된다.

As a Team Member

기술적인 성장보다 내게 더욱 큰 영향을 미친 것은 팀 멤버로서 어떻게 일하고 협업하며 신뢰를 얻는지에 대해 배웠다는 것이다. 나는 지금껏 내 스스로 일을 잘 한다고 생각해본 적이 거의 없다. 내가 실제로 수행할 수 있는 작업의 양과 비교하여 주어진 작업이 많거나 과중하다는 것을 알고 있고, 이를 적절히 쪼개서 우선순위대로 진행하는 것이 가장 중요함을 알고 있음에도, 내 앞에 놓여있는 밀린 작업들을 보거나 스스로 생산성이 크게 저하되었음을 느끼면 위기감이 이성을 지배하기 시작한다. 그러면 도움이 안되는 것을 알면서도 조금씩 오버런을 하게 되고, 이것이 장기화되면 번아웃으로 이어진다. 나는 이것이 내가 판단하는 나의 퍼포먼스를 내 스스로 신뢰하지 못하거나, 내 팀원들은 나와 다르게 생각할 것이라고 추측하기 때문에 일어난다고 생각했다.

이럴 때 가장 도움이 됐던 것은 팀원들의 피드백을 통해 내 퍼포먼스에 대해 객관적으로 돌아보는 것이었다. 덕분에 팀원들이 나를 신뢰하고 있다는 것과 나의 퍼포먼스에서 장점과 개선점을 객관적으로 정리할 수 있었고 오히려 쉴 타이밍에 여유롭게 휴식을 취할 수 있게 되면서 생산성이 더욱 늘어남을 체감할 수 있었다. 이러한 과정들을 겪고 3개월의 시간을 보내고 나니 나 또한 다른 사람들에게 신뢰를 주고 생산성을 올리는 투명하고 객관적인 피드백을 나눌 수 있는 팀원이 되고 싶다고 생각하게 되었고, 앞으로 그런 역할을 할 수 있도록 노력하려 한다. 내가 내 동료들에게 고마운 만큼, 나도 누군가에게 고마운 팀원이 되고 싶다.

Misc

여기부터는 내가 개인적으로 어떤 시간을 보냈고 앞으로의 액션 플랜을 적어보고자 한다.

글 쓰기

마지막 글이 올라간 날짜가 2월 5일이었는데, 2월 8일에 입사 후 놀랍게도 4개월 후에 첫 글을 작성했다… 블로그에 신경쓰지 못한 것이 가장 후회스럽다. 무려 일주일에 하나씩 쓰자는 글까지 써놓고 이렇게 손을 대지 못하게 될 줄은… 오늘 이 글을 쓰면서 글을 쓰는 것도 생각을 정리하는 시간을 가지기에 정말 좋다는 생각이 들어서 이젠 정말로 주말마다 조금씩이라도 글을 써보려 한다. 나의 개발자로서의 성장의 발자취가 모두 남을 수 있도록, 배운 것들 하나하나가 글로 남길 수 있으면 좋겠다고 생각한다.

운동하기

코로나를 핑계로 운동을 오랫동안 안나갔더니 몸이 점점 흐물흐물해짐을 느끼고 있다. 1~2주 전에는 체력이 심각하게 부족해서 일하는 것에 지장이 감을 느끼기도 했었는데, 앞으로 이래서는 안되겠다고 생각하고 조금씩이라도 운동을 시도해보려고 한다. 운동할 시간을 내는 것도 생활 패턴이 일정해지는 것과 같이 가기 때문에, 지속가능하고 건강한 개발 생활이 궤도에 오르려면 꽤 오랜 시간이 걸릴 것 같다. 정시에 일어나고, 정시에 자고, 밥 먹을때 밥 먹는 것이 왜 이리 힘든지 모르겠다.

취미 찾기

운동과 글 외에도 집에 들어왔을 때 손에 잡을 수 있는, 개발이 아닌 무언가가 필요함을 느끼고 있다. 지난 3일간 내가 뭘 하고 지냈는지를 생각해봤는데 정말 떠오르는 게 하나도 없어서 충격이었다. 첵 읽기, 영화나 드라마 보기, 그림 그리기 등 여러 가지를 생각하고 꼭 짬을 내서 조금씩 실행해 볼 생각이다.

© 2020 by appleseedPowered by
React Logo