sbyeol3/articles

[번역] 유지보수 가능한 자바스크립트 코드 작성하기 2023 - Web, Node.js

Opened this issue · 0 comments

원문 : How to write maintainable JavaScript code in 2023 — Web or Node.js

어떠한 어리석은 사람이라도 누구나 컴퓨터가 이해할 수 있는 코드는 쓸 수 있다. 좋은 프로그래머들은 사람이 이해할 수 있는 코드를 쓴다.
- Martin Fowler

2022년에도 여전히 자바스크립트 개발은 혼란스럽습니다. 컴퓨터는 날로 빨라지고 있기에, 우리는 기계가 아닌, 프로그래머를 위한 코드를� 작성할 수 있게 되었습니다. 이는 곧, 자바스크립트 세계가 되어야 하는 것이기도 합니다.

주의: 이 글은 최소 중간 레벨의 경험을 가진 JS 개발자(웹, Node.js)에게 가장 적합합니다. 저는 독자들이 JS를 쓴 경험이 있다는 전제하에 가정하고 있습니다.

목차

  1. Use TypeScript everywhere
  2. Learn your JavaScript and frameworks well
  3. Code formatting and style
  4. Comments in code and inline documentation
  5. Write meaningful and useful tests where applicable
  6. Use prototypes and/or MVPs before implementing complex features
  7. Bonus: A few extra tips

Use TypeScript everywhere

타입스크립트를 두려워 하지 마세요. 어느정도 꽤 숙련된 자바스크립트 개발자가 the official 5-minute guide “TypeScript for JavaScript Programmers”를 본다면 오늘 당장이라도 타입스크립트를 쓰기 시작할 수 있습니다.

타입스크립트는 여러분이 바로 다시 시작하게끔 도와줍니다.

여러분이 자동완성 기능, 제안, 인라인 문서 등의 기능을 가진 IDE나 에디터를 사용하고 있다면 여러분은 TypeScript™를 경험하시게 됩니다. 어떤 맥락없이 다음 두 개의 예제를 살펴봅시다.

// src/api/posts.js

export default {
  getPosts: () => axios.get("/posts"),
}

위 코드의 문제점은 아마 바로 보이실 겁니다. getPosts는 무엇은 리턴하나요? 만약 여러분이 axios에서 fetch로 전환해야 한다면 어떨까요? post를 생성하는 함수를 추가하고 싶다면 어떨까요? 객체의 data의 구조를 어떻게 정의해야 할까요? 동일한 코드를 타입스크립트로 작성한 것을 아래에서 보여드리겠습니다.

// src/api/posts.ts
export interface Post {
  title: string;
  excerpt: string;
}
export default {
  getPosts: function(): Promise<AxiosResponse<Post[]>> {
    return axios.get("/posts");
  },
};

가장 중요한 차이점은 투명성입니다. 여러분은 코드를 바로 이해할 수 있고 여러분의 에디터나 tsc가 도움을 줄 겁니다. getPosts을 TS 버전으로 호출하게 되면 여러분은 바로 유용한 정보들을 얻을 수 있습니다.

image

타입스크립트에 올인을 외치게 되는 이유입니다. 서버와 클라이언트 간에 공유되는 모델을 사용하면 양쪽에서 동일한 구조를 적용할 수 있습니다. 객체 프로퍼티나 클래스에 타입을 강제하면 여러분의 코드의 오타를 방지할 수 있습니다. 타입스크립트 덕분에 잡아낼 수 있는 버그들은 아주 다양합니다. 이뿐만 아니라 2년 안에 다시 그 코드를 수정하게 될 수도 있습니다.

Learn your JavaScript and frameworks well

사람은 도구만 있으면 된다. — Emmert Wolf

2022년에는 자바스크립트가 뒤에서 무슨 일을 하고 있는지 알아야 합니다. 만약 잘 알지 못한다면 제가 가장 좋아하는 자바스크립트 자료인 무료 e북 Eloquent JavaScript을 읽어보세요. 또한 여러분은 자바스크립트가 오늘날 원시 데이터 구조와 신뢰할 수 없는 브라우저 API들을 굉장히 많이 제공함을 알아야 합니다. 예를 들어 이제 여러분은 next-gen JavaScript, Keyed Collections, 이미 잘 지원되고 있는 Internationalization API를 사용할 수 있고 서드파티에 대한 의존성을 줄임으로써 CPU 타임을 아낄 수 있습니다.

image

멋진 JS 개발자가 되기 위해서 비동기 자바스크립트, 제너레이터 함수, 함수형/객체지향 패러다임, 스코프, 성능 등 많은 것들을 이해하고 있어야 합니다. 그러나 불행히도 대부분의 JS 개발자들은 비동기 자바스크립트에 대해 깊은 이해를 갖지 못했습니다. 비동기 자바스크립트는 모던 앱에서 아주 핵심적인 기능인데도 말입니다(Promise와 async/await을 생각해보세요).

여러분들이 새로운 걸 배우지 않고도 오늘 시작할 수 있는 것들을 알려드리겠습니다.

  1. 문서와 함께 작업을 해보세요. Next.js, Webpack, React, TypeScript, 그리고 다른 툴들의 문서들이 있습니다. 예를 들어 React는 우리가 한번도 사용하지 않았던 많은 hook들을 가지고 있습니다. 주요 기능을 개발하거나 프로젝트를 셋업해야 할 때 여러분이 사용하는 도구의 문서를 깊이 들여다보시길 권장합니다.
  2. 코드 성능을 테스트하세요. profiler, debugger, 시간 측정, 과장된 테스트 등을 이용할 수 있습니다. 10만 행의 데이터를 리액트 테이블에 넣어보는 건 어떨까요? 주요 기능을 출시하기 전에 좋은 가독성 뿐만 아니라 _성능_도 목표로 삼아 보세요. 최악의 상황을 테스트하면 여러분의 코드가 성능의 병목 지점인지 아닌지 확인할 수 있습니다. 예를 들어, 저는 여러분들이 for loop와 Array.prototype.forEach의 차이를 아셨으면 좋겠습니다. 무거운 연산 작업이나 큰 데이터 셋을 가진 비동기 자바스크립트에 유용하기 때문이죠.
  3. 모든 것에 대해 너무 NPM에 의존하지 마세요. 간단한 라이브러리 대부분은 여러분도 쉽게 구현할 수 있습니다. 2016년 left-pad fiasco를 기억하시나요? 패키지 업데이트, 호환성 테스트, 추가적으로 필요한 코딩의 귀찮음으로부터 여러분을 해방시켜 보세요.
  4. 라이브러리를 문서 없이 사용한다면 소스코드를 읽어보세요. 더 문서화가 잘되고 인기있는 라이브러리를 고수하는 것도 방법이 될 수 있습니다.
  5. 여러분의 번들 사이즈에 영향을 미치는 것들을 분석하세요. _.omit을 호출하기 위해서 전체 lodash 라이브러리가 필요하진 않습니다. 거대한 번들을 로딩하는 것은 여러분의 초기 로딩 속도를 현저히 느리게 만듭니다. 번들 사이즈에 대해 더 알고 싶다면 Kasra가 작성한 (웹팩에 초점을 둔) 좋은 아티클을 읽어보세요.

Code formatting and style

이 부분은 잘 간과되기도 하지만 개발에서 아주 중요합니다. 혼자 일하든, 팀으로 일하든지에 관계없이 몇몇 룰을 강제하는 건 도움이 됩니다.

  1. ESLint와 Prettier를 같이 사용하면 여러분의 코드의 역할에만 집중할 수 있습니다. 겨우 콤마 하나를 빠뜨려 자동 CI 테스트에서 실패한다고 상상해보세요. 또한 Prettier의 놀라운 기본값들을 사용하면 한번에 코드 스타일 옵션을 끝낼 수 있습니다.
  2. 변수에 설명이 되는 이름을 붙이세요. 이름만 읽었을 때 뭔지 알 수 있어야 합니다. buttonDomElemel이 있으면 무엇이 더 이해하기 쉬운 이름인가요? loadingisUserDataLoading은 어떤가요? boolean 변수에는 ishas와 같은 접두사를 붙이고 긴 변수 이름을 두려워 하지 마세요. IDE는 이름을 제안하고 vim 유저는 변수 이름을 빠르게 복사할 수 있습니다.
  3. 띄어쓰기를 활용하세요. Prettier를 사용하면 Prettier는 여러 줄을 끊어주기도 합니다. 아주 좋죠. 그러나 한 줄 구분하는 것이 더 도움이 된다면 함수 내부나 객체 선언에서도 사용 가능합니다.

Comments in code and inline documentation

TSDoc이나 JSDoc을 함께 사용함으로써 타입스크립트를 안써도 생각할 수도 있지만, 저는 절대 그렇게 생각하지 않습니다. 타입스크립트와 JSdoc을 함께 사용하면 앱의 다른 곳에서 호출을 실행할 때마다 어떤 일이 발생하는지 모두가 이해할 수 있습니다. 키워드나 문서의 형태든 가독성만 좋다면 원하는 형식을 사용하면 됩니다.

코드의 복잡한 부분을 작성할 때는, 절대 여러분들이 질문에 대답할 수 없는 개발자들이 읽는다는 것을 항상 가정하고 코드를 작성하셔야 합니다.
2년 후 동일한 프로젝트에서 버그를 수정하러 다시 돌아온다 하더라도, 전체 소스코드를 읽지 않고도 어떻게 동작하는지 이해할 수 있을 겁니다.

실행 주기가 종료될 때까지 지연되는 설명도 없이 setTimeout(someFn, 0)라는 코드를 본 적이 있나요? 그게 어떤 일을 하는지 알겠지만, 왜 표의 유저 아바타에 색을 매핑하는 것처럼 랜덤하게 호출되는 걸까요? 바로 여기에서 사려 깊은 주석이 있다면, 이미 수정된 버그를 다시 발견하는 고통을 덜어주게 될 겁니다.

Write meaningful and useful tests where applicable

당신의 제품을 유닛 테스팅하는 것을 좋아하지 않는다면, 당신의 고객 또한 테스트를 좋아하지 않을 가능성이 높다

아마 여러분들은 TDD 패러다임에 대해 들어보셨을 겁니다. 보통 TDD를 강제화하는 개발자들과 TDD를 경멸하는 개발자들로 나뉘곤 합니다.

저는 TDD에 대해 찬성도, 반대도 하지 않습니다. 때때로 TDD가 반드시 필요한 경우가 있고 또 반대로 자원을 낭비하기도 합니다. 특정 스펙을 기반으로 하는 중요한 백엔드 기능을 개발하고 있다면 TDD로부터 오는 이점들이 많습니다. 그러나 프론트엔드 대시보드 앱을 개발하고 있다면 TDD를 적용하는 것은 개발 속도를 아주 늦춰버립니다. 장단점을 고려해보고 선택하세요.

  1. Unit testing은 대부분의 사용 사례에 대한 직접적인 테스팅 방법입니다. 프론트엔드 앱을 유닛 테스트하는 것은 레이아웃, 모달 다이얼로그, 위저드, 다단계 클라이언트사이드 유효한 폼 등의 복잡한 컴포넌트를 평가할 수 있습니다. 백엔드 개발을 할 땐 엔드포인트가 올바른 경우와 잘못된 경우 모두 예상하는 방식으로 잘 동작하는지 검증할 수 있습니다.
  2. End-to-end (E2E) testing은 테스트의 진짜 MVP입니다. 일단 프론트엔드 어플리케이션이 너무 커지면 테스트 DB를 만들고 릴리즈 때마다 실제 브라우저에서 실제 코드를 테스트하는 것이 합리적입니다. 프론트엔드에서 E2E 테스트를 수행하는 가장 포괄적인 방법은 UX 엔지니어처럼 생각하는 것입니다. 유저 플로우를 개발하거나 HotJar와 같은 툴의 데이터를 모으고 유저가 하는 것들을 테스트합니다.

원한다면 더 많은 것들을 테스트할 수도 있죠. 큰 프로젝트에서 package.json의 일반적인 테스트 스크립트는 "test": "NODE_ENV=test eslint && tsc --noEmit && jest && cypress run"처럼 생겼습니다.

많은 테스트 !== 좋은 테스트

여러분의 테스트를 유용성에 초점을 맞춰야 합니다. 간단한 버튼이나 유틸리티 함수를 유닛 테스팅하는 데 시간을 낭비하지 마세요. 이렇게 하면 이미 가지고 있는 기능들에 대한 테스트를 막게 됩니다.

많은 기획자들이나 시니어 개발자들은 코드 커버리지 지표에 매달립니다. 코드 커버리지는 경험에 기반되어야 합니다. 높은 코드 커버리지를 강제하게 되면 개발자가 시간을 절약하기 위해 서드파티 라이브러리를 사용하게 만들고, 이것의 단점은 이미 위에서 이야기했습니다.

Use prototypes and/or MVPs before implementing complex features

명확한 스펙사항을 갖춘 투명한 프로젝트의 라이프사이클을 설정하고 각 단계를 언제 출시할지 안다면, 이미 끝난 작업들을 다시 돌아볼 수 있는 기회들을 많이 얻을 수 있습니다. 이는 아주 좋습니다. 일단 MVP가 "끝나면", 하던 일로부터 나의 주관을 분리하고 결과를 평가할 수 있기 때문입니다. 이때가 추가적인 성능 테스팅, 마이너한 리팩토링이나 코드의 퀄리티를 높일 수 있는 좋은 타이밍입니다.

image

주요한 기능을 개발하고 작은 부분을 클린 업하고 리팩토링하세요.

이 규칙은 여러분의 프로젝트가 몇 달 이상 지속되는 경우에만 유효합니다. 일단 주요 기능이 끝나면, 여러분의 마인드셋을 바꾸고 신선한 냉소적인 시각을 가지고 돌아가세요. 병목지점, 경고, 개선점을 찾고 무엇을 언제 바꾸는게 의미있을지 평가하세요.

export된 JS 객체의 프로퍼티인 함수 모음을 만드는 것이 가장 간단한 예시입니다. 참조를 유지하는 한 자바스크립트의 기본적인 가비지 컬렉터가 컬렉팅을 하지 못하므로 매번 전체 객체를 가져올 필요는 없습니다. 몇 가지 순수한 기능만 있다면 괜찮을 겁니다. 그러나 6곳에서 70개의 순수하지 않은 함수를 사용한다면 여러분의 앱은 아주 느려질 겁니다. 대신에 이름이 지정된 export를 작은 파일로 만들면 사용하지 않는 코드가 수만 줄 존재하는 위험을 줄일 수 있습니다.

기능을 테스트하고 끝없는 리팩토링을 피하기 위해선 프로토타입을 사용하세요.

여러분의 가장 못생기고 유지보수가 불가능한 코드가 있는 곳이 별도의 단일 목적 프로토타입 프로젝트가 될 수 있습니다. 예를 들어 처음으로 WebRTC로 작업한다면 프로토타입으로 작업하세요. 작업한 부분이 요구사항을 잘 이행하는게 검증되었다면 실제 코드로 옮길 때입니다. 이 시점에서 코드가 어떻게 보이고 동작하기를 원하는지에 대한 더 명확한 비전이 있어야 합니다.

Bonus: A few extra tips

  1. 작업하는 모든 프로젝트의 초기 설정, 배포 지시사항을 요구사항과 버전을 포함하여 문서화하고, 어떤 순서로 수행해야 하는지 설명하세요. 이유도 적어두면 더 좋습니다.
  2. 관행을 세우고 이를 기반으로 앱을 구성하세요.이전의 MVC 접근 방식을 빌려올 수도 있고, 기능을 분할하거나 여러 접근방식을 섞어서 시도해볼 수도 있습니다. 이에 옳고 그름은 없습니다.
  3. 여러분의 경제상황에 따른 가장 좋은 도구를 사용하세요. 여러분이 즐기면서 사용할 수 있고 사무실을 꾸밀 수 있는 하드웨어를 구입하고, 여러분의 일을 즐겁게 하는 것에 투자하세요. 그러고 나면 여러분은 하루의 1/3을 일하는 데 보낼 수 있습니다.
  4. 비현실적인 데드라인을 맞추기 위해 자신을 푸쉬하지 마세요. 근무환경이 너무 힘들면, 하나의 일이 당신의 창의력과 열정을 죽이게 놔두지 마세요. 언제든 다른 자리로 옮길 수 있습니다.
  5. 당신의 의견을 표현하고 논쟁하세요. "바보같은 질문은 없다"는 말을 기억하십니까? 그렇다면 계속 질문하고 말하세요. 많이 의견을 나눌수록 당신의 사고가 확장됩니다.

유지보수하기 쉬운 코드를 작성하는 자세한 팁들이 굉장히 많습니다. 해피코딩하세요!