컴포지션 API를 만든 이유
Composition API 제안에 반대하는 유저의 글에 대한 Evan You의 답변 번역
저는 당신의 의견을 존중하므로 다음 내용을 어느 것도 개인적인 것으로 받아들이지 마세요.
결국 정말 중요한 문제들은 전혀 해결되지 않기때문에 이 급진적인 변화는 정당화할 수 없습니다. 본질적으로 이 제안은 그저 겉만 번지르르한 새로운 것을 쫓는 것과 같습니다.
저는 이 말에 동의할 수 없습니다. Logic Composition은 아마도 프로젝트 확장 측면에서 가장 중요한 문제 중 하나일 것입니다. 다른 스레드에서 제가 썼던 말을 다시 인용합니다:
- 이 RFC 스레드(Composition API 제안. 이하 RFC)에서는 많은 유저들이 정확히 본인들이 현재 직면한 문제를 해결해줄 수 있다고 말하고 있습니다. 그리고 그들은 또한 현재의 object-based syntax(Options API)가 프로젝트의 확장성을 방해하는 병목 현상이라고 언급했습니다. 나는 여러분 중 많은 사람들이 이것을 개인적으로 경험한 적이 없다는 것을 인정합니다(완전히 맞는 말이고, 심지어 훌륭합니다!). 그러나 ‘미학적인’ 선택이 실제 유지 관리 부담이 된다면(물론 특정 유형의 프로젝트에서만), 더 이상 단순한 문제가 아닙니다. 더 나은 추상화로 문제를 해결할 수 있는 기회를 일부 사용자의 ‘미적’ 선호로 인해 거부하는 것은 잘못된 절충안처럼 보입니다.
Vue는 작게 시작했지만 오늘날에는 다양한 수준의 복잡성과 비즈니스 영역을 포함하는 매우 광범위한 프로젝트에서 사용되고 있습니다. 다양한 유형의 프로젝트를 처리하는 개발자는 다양한 요구 사항에 직면하게 되며, 일부는 object-based syntax API를 사용하여 쉽게 처리할 수 있지만 일부는 그렇지 않습니다. 주요 예시들로
- 수백 줄 길이의, 다양한 로직을 포함하고 있는 대형 컴포넌트
- 여러 컴포넌트 간에 공통 로직을 공유할 필요성
(1) 에 해당하는 상황에서는 로직들이 강제로 각 옵션 유형들에 나눠져 할당됩니다. 예를 들어, 하나의 data fetching 작업이 prop
, data
, computed property
, mounted hook
그리고 watcher
까지 필요할 수도 있습니다. 즉, 컴포넌트에서 하나의 data fetching 과정을 이해하려고 할 때 옵션 목록에서 계속 위아래로 점프하게 됩니다. 동시에 속성을 훑어볼 때 속성이 어떤 유형 인지 알고 있지만 처리해야하는 로직 을 말하기는 상당히 어렵습니다. 컴포넌트에 더 많은 로직들이 추가되면 상황이 악화됩니다. 이에 비해 Composition API를 사용하면 한 data fetching을 위한 모든 관련 로직을 그룹화할 수 있으며 더 중요한 것은 별도의 함수 또는 별도의 파일로 깔끔하게 추출할 수 있다는 점입니다.
이 문제는 프로젝트의 파일 구성으로 비유할 수 있습니다. 우리 중 많은 사람들이 파일 유형별로 파일을 구성하는 것(예: 모든 것을 html
, js
및 css
폴더로 분할)이 확장성이 없다는 데 동의합니다. 한가지 기능과 관련된 코드들이 잘못 이해한 "관심사 분리(seperation of concerns)"를 위해 세 개의 폴더로 강제로 분할됩니다. 여기서 핵심은 "관심사"가 파일 형식으로 정의되지 않는다는 것입니다. 대신 우리 대부분은 기능이나 책임(responsibility)별로 파일을 구성합니다. 이것이 바로 사람들이 Vue 단일 파일 구성 요소(SFC)를 좋아하는 이유입니다. SFC는 기능별로 코드를 구성하는 방법입니다. 아이러니하게도 SFC가 처음 도입되었을 때 많은 사람들이 SFC가 관심사 분리에 위배된다고 생각하여 반대했지만 나중에 SFC가 실제로 관심사를 분리하는 더 합리적인 방법이라는 것을 인정했습니다.
(2)는 RFC의 ‘동기 (Motivation)’ 섹션에서 중요하게 설명했는데, mixins/HOCs/scoped slots이 할 수 있는 것들을 단점없이 똑같이 해낼 수 있다는 것을 보여줍니다.
React Hook을 통해 우리는 Hook의 특성 중 일부가 위에서 언급한 문제들을 해결하는 데 도움이 될 수 있음을 발견했습니다. 이것이 우리가 이 제안(Composition API)을 하게 된 근본적인 이유입니다. 그야말로 "새로운 것"이지만, 우리는 그것이 "새롭다"는 이유가 아니라 객관적으로 존재하는 문제에 대한 해결책을 제시하기 때문에 새로운 것을 채택하고 있습니다. 장기적으로 이 새로운 API의 가능성이 위에서 언급한 문제들을 다루는 개발자들의 시간을 절약해 막대한 이익을 가져다 줄 것입니다.
Type Safety도 중요한 고려 사항입니다. 다시 말하지만, 이 사항 또한 많은 사용자들이 간절히 원했지만 TypeScript를 사용하지 않는 사람들에게는 가치가 없어 보일 수 있습니다. 이해할 수 있습니다. 그러나 해결되는 문제가 당신에게 영향을 미치지 않기 때문에 어떤 문제도 해결되지 않는다고 주장하는 것은 약간 이기적인 것이라고 생각합니다.
모든 종류의 프로젝트에 급진적인 API 변경 사항을 도입하는 것은 급격한 변화(Breaking Changes)이며, 이전 API와의 역호환성을 관대하게 제공해도 이 문제가 해결되지 않습니다.
"Breaking"은 ‘사용자가 강제로 코드를 변경해야함’으로 정의됩니다. 사용자는 기존 코드를 변경할 필요가 없으므로 Breaking이 아닙니다. 이 부분에 대해서는 더 이상 반박할 여지가 없다고 생각합니다.
이전 버전과의 호환성으로도 충분하지 않다면, 본질적으로 프로젝트가 급진적인 새로운 아이디어를 도입해서는 안 된다고 말하는 것입니다. 저는 이것이 프로젝트 정책 수준의 논쟁이라고 생각합니다. 만약 제가 투표를 한다면, 저는 단호하게 반대할 것입니다. 우리는 사용자의 최선의 이익을 염두에 두고 최선을 다할 것이지만 프로젝트는 발전해야하고 발전할 것입니다.
이런 식으로 일을 하는 것은 불가사의하고 비잔틴이나 미로 같은 "스파게티 코드"로 이어질 가능성이 더 큽니다. Vue의 가장 큰 장점 중 하나는 단순함과 접근성입니다. Vue 주요 [변덕스럽고 종잡을 수 없는 API 변경과 아키텍처 문제로 인해 다른 많은 JavaScript 관련 약속이 그러하듯이 Vue에 대한 주요 조직의 약속은 손에서 증발하기보다는 유통 기한과 내구성을 가질 만큼 충분히 정적입니다.]
반대로, 이 제안의 동기는 장기적으로 Vue 프로젝트의 유지보수성을 향상시키는 것이었습니다.
JavaScript 프로젝트를 살펴보면 모든 코드는 한 시작지점 파일에서 시작하며, 본질적으로 암시적으로 "main" 함수가 호출되면서 시작합니다. 단일 함수 시작점이 스파게티 코드로 이어지는 것이라면 모든 JavaScript 프로젝트는 스파게티 코드여야 합니다. 이는 분명히 사실이 아닙니다. 왜냐구요? 개발자로서 우리는 코드를 모듈 또는 더 작은 기능으로 분할하여 구성하는 방법을 배웠기 때문입니다.
함수 기반 API 디자인의 핵심 특징은 setup()
내 코드를 이해한다는 것이 관용적인 JavaScript 코드를 이해하는 것과 다르지 않으며, 일반 JavaScript 코드를 구성하는 데 사용할 수 있는 모든 기술을 사용해 setup()
함수를 구성할 수 있다는 것입니다. 팀에서 일반적인 JavaScript 코드에 사용하는 모든 지식/스타일 가이드/코드 검토 프로세스를 Vue setup()
함수의 코드에 적용할 수 있습니다.
저는 새로운 API가 이론적으로 코드 품질에 대한 임계값이 더 낮을 수 있음에 동의합니다. 그러나 언급한 바와 같이 Vue가 아닌 코드베이스에서 스파게티 코드를 방지하기 위해 이미 수행하고 있는 모든 작업들을 적용할 수 있으므로 완화될 수 있습니다. 반면에 새로운 API로 작성된 코드는 코드 품질의 상한선도 상당히 높습니다. 새로운 API로 작성된 모든 코드는 옵션 기반 API보다 훨씬 더 높은 품질의 코드로 리팩토링될 수 있지만 옵션 기반 API를 사용하면 믹스인에 의존해야하고 그 단점들도 처리해야 합니다.
저는 또한 이 RFC가 유지 보수성을 위해 단순성을 거래하는 것에 관한 것이 아니라는 점을 지적하고 싶습니다. 알아야할 것은 지금 몇 년 동안 사용했을 수도 있는 API와 처음 본 API간의 느낌을 비교하고 있다는 사실입니다. 기본적으로 아래가 컴포넌트에 관한 관점의 전환입니다.
- 옵션 기반 API는 컴포넌트를 컴포넌트가 포함한 속성/메서드/옵션이 정의된 유형에 기반해 이해합니다.
- 함수 기반 API는 컴포넌트를 캡슐화하는 논리적 주제에 기반해 이해합니다.
많은 사용자가 "단순함을 잃는다"고 말할 때 한탄하는 것은 사실 옵션 유형별로 컴포넌트를 살필 수 있는 능력을 잃는 것에 대해 말하는 것입니다. 그러나 새 API를 사용하면 컴포넌트를 옵션 유형별로 살필 수 있는 능력을 제공하는 분석기를 구현하는 것이 매우 간단합니다. 즉, 새 API에서는 두 가지 관점 모두에서 컴포넌트를 살필 수 있지만 옵션 기반 API를 사용하면 하나로 제한됩니다 (옵션 간에 분할할 때 논리적 주제에 대한 의도가 손실되기 때문에).
Vue를 좋게 만드는 것들을 버릴 가치가 없습니다.
"아무것도 버리지 않는다"는 말을 되풀이하는 것도 지겹습니다. 그러나 "Vue를 좋게 만드는 것"이 실제로 무엇인지 정의하려고 합니다. 이 RFC에 반대하는 많은 사용자는 그것을 object-based syntax로 정의하고 object-base syntax를 제거하면 Vue를 Vue스럽게 만드는 모든 것을 제거하는 것처럼 보인다고 말합니다. 그러나 그대로 남아 있는 것을 살펴보겠습니다.
- 템플릿 구문은 변경되지 않습니다 (심지어 성능이 향상되고 있습니다!)
- 반응성 시스템이 작동하는 방식은 변경되지 않습니다.
- computed 속성, watcher 및 컴포넌트 life cycle의 개념은 변경되지 않습니다.
- SFC 형식은 변경되지 않습니다.
- CLI가 변경되지 않습니다.
- 프레임워크의 진보적 성격은 변하지 않습니다.
- 더 나은 개발 도구를 제공하겠다는 팀의 약속은 변하지 않습니다.
- 사실 기술적으로, object format도 변경되지 않습니다. 작동하던 모든 것들은 계속 작동합니다.
Vue의 object-based syntax는 처음부터 존재해왔습니다. 위의 많은 것들은 나중에 추가되었으며 각각 Vue의 성장에 기여했습니다. object-based syntax이 당신에게 중요한 전부라고 생각하신다면 한 걸음 물러서서 Vue를 실제로 만드는 것이 무엇인지 다시 생각해 보시기 바랍니다. 결국 이 RFC는 보이는 것처럼 급진적인 변화가 아닙니다.