선언적 프로그래밍을 활용하여 유지 관리 가능한 웹 앱 만들기

게시 됨: 2022-03-11

이 기사에서는 선언적 스타일의 프로그래밍 기술을 현명하게 채택하여 팀이 확장 및 유지 관리가 더 쉬운 웹 응용 프로그램을 만들 수 있는 방법을 보여줍니다.

"...선언적 프로그래밍은 제어 흐름을 설명하지 않고 계산 논리를 표현하는 프로그래밍 패러다임입니다." —Remo H. Jansen, TypeScript를 사용한 함수형 프로그래밍 실습

소프트웨어의 대부분의 문제와 마찬가지로 애플리케이션에서 선언적 프로그래밍 기술을 사용하기로 결정하려면 트레이드오프를 신중하게 평가해야 합니다. 이에 대한 심층 토론은 이전 기사 중 하나를 확인하십시오.

여기서는 다중 패러다임을 지원하는 언어인 JavaScript로 작성된 신규 및 기존 애플리케이션 모두에 선언적 프로그래밍 패턴을 점진적으로 채택하는 방법에 중점을 둡니다.

먼저 백 엔드와 프런트 엔드 모두에서 TypeScript를 사용하여 코드를 보다 표현력 있고 변경에 탄력적으로 만드는 방법에 대해 설명합니다. 그런 다음 FSM(유한 상태 기계)을 탐색하여 프런트 엔드 개발을 간소화하고 개발 프로세스에서 이해 관계자의 참여를 높입니다.

FSM은 새로운 기술이 아닙니다. 그들은 거의 50년 전에 발견되었으며 소프트웨어 정확성이 중요할 수 있는 신호 처리, 항공 및 금융과 같은 산업에서 널리 사용됩니다. 또한 복잡한 비동기 상태 업데이트 및 애니메이션 조정과 같이 최신 웹 개발에서 자주 발생하는 모델링 문제에 매우 적합합니다.

이 이점은 상태가 관리되는 방식에 대한 제약으로 인해 발생합니다. 상태 머신은 동시에 하나의 상태에만 있을 수 있으며 외부 이벤트(예: 마우스 클릭 또는 가져오기 응답)에 대한 응답으로 전환할 수 있는 제한된 인접 상태를 가집니다. 그 결과 일반적으로 결함률이 크게 감소합니다. 그러나 FSM 접근 방식은 대규모 애플리케이션에서 제대로 작동하도록 확장하기 어려울 수 있습니다. statechart라고 하는 FSM에 대한 최근 확장을 통해 복잡한 FSM을 시각화하고 훨씬 더 큰 응용 프로그램으로 확장할 수 있습니다. 이는 이 기사에서 초점을 맞춘 유한 상태 기계의 특징입니다. 데모를 위해 JavaScript의 FSM 및 상태 차트를 위한 최고의 솔루션 중 하나인 XState 라이브러리를 사용할 것입니다.

Node.js를 사용하여 백엔드에서 선언적

선언적 접근 방식을 사용하여 웹 서버 백엔드를 프로그래밍하는 것은 큰 주제이며 일반적으로 적절한 서버 측 기능 프로그래밍 언어를 평가하는 것으로 시작할 수 있습니다. 대신 백엔드로 Node.js를 이미 선택(또는 고려 중)했을 때 이 글을 읽고 있다고 가정해 보겠습니다.

이 섹션에서는 다음과 같은 이점이 있는 백엔드에서 엔터티 모델링에 대한 접근 방식을 자세히 설명합니다.

  • 코드 가독성 향상
  • 더 안전한 리팩토링
  • 보증 유형 모델링이 제공하는 성능 향상 가능성

유형 모델링을 통한 행동 보장

자바스크립트

JavaScript에서 이메일 주소를 통해 주어진 사용자를 찾는 작업을 고려하십시오.

 function validateEmail(email) { if (typeof email !== "string") return false; return isWellFormedEmailAddress(email); } function lookupUser(validatedEmail) { // Assume a valid email is passed in. // Safe to pass this down to the database for a user lookup.. }

이 함수는 이메일 주소를 문자열로 받아들이고 일치하는 경우 데이터베이스에서 해당 사용자를 반환합니다.

lookupUser() 는 기본 유효성 검사가 수행된 후에만 호출된다고 가정합니다. 이것은 핵심 가정입니다. 몇 주 후에 일부 리팩토링이 수행되고 이 가정이 더 이상 유효하지 않다면 어떻게 될까요? 단위 테스트가 버그를 잡거나 필터링되지 않은 텍스트를 데이터베이스에 보낼 수 있다고 손가락질했습니다!

TypeScript(첫 번째 시도)

유효성 검사 기능에 해당하는 TypeScript를 생각해 보겠습니다.

 function validateEmail(email: string) { // No longer needed the type check (typeof email === "string"). return isWellFormedEmailAddress(email); }

이것은 TypeScript 컴파일러가 추가 런타임 유효성 검사 단계를 추가하지 않아도 되도록 약간 개선되었습니다.

강력한 타이핑이 가져올 수 있는 안전 보장은 아직 실제로 활용되지 않았습니다. 그것에 대해 살펴보겠습니다.

TypeScript(두 번째 시도)

유형 안전성을 개선하고 처리되지 않은 문자열을 looukupUser 에 대한 입력으로 전달하지 않도록 합시다.

 type ValidEmail = { value: string }; function validateEmail(input: string): Email | null { if (!isWellFormedEmailAddress(input)) return null; return { value: email }; } function lookupUser(email: ValidEmail): User { // No need to perform validation. Compiler has already ensured only valid emails have been passed in. return lookupUserInDatabase(email.value); }

이게 더 좋은데 번거롭습니다. email.value 의 모든 사용은 ValidEmail 를 통해 실제 주소에 액세스합니다. TypeScript는 Java 및 C#과 같은 언어에서 사용하는 명목 형 형식 대신 구조적 형식 지정을 사용합니다.

강력하지만 이는 이 서명을 준수하는 다른 모든 유형이 동등한 것으로 간주됨을 의미합니다. 예를 들어 컴파일러의 불만 없이 다음 비밀번호 유형을 lookupUser() 에 전달할 수 있습니다.

 type ValidPassword = { value: string }; const password = { value: "password" }; lookupUser(password); // No error.

TypeScript(세 번째 시도)

Intersection을 사용하여 TypeScript에서 명목형 타이핑을 달성할 수 있습니다.

 type ValidEmail = string & { _: "ValidEmail" }; function validateEmail(input: string): ValidEmail { // Perform email validation checks.. return input as ValidEmail; } type ValidPassword = string & { _: "ValidPassword" }; function validatePassword(input: string): ValidPassword { ... } lookupUser("[email protected]"); // Error: expected type ValidEmail. lookupUser(validatePassword("MyPassword"); // Error: expected type ValidEmail. lookupUser(validateEmail("[email protected]")); // Ok.

이제 검증된 이메일 문자열만 lookupUser() 에 전달할 수 있다는 목표를 달성했습니다.

전문가 팁: 다음 도우미 유형을 사용하여 이 패턴을 쉽게 적용하십시오.

 type Opaque<K, T> = T & { __TYPE__: K }; type Email = Opaque<"Email", string>; type Password = Opaque<"Password", string>; type UserId = Opaque<"UserId", number>;

장점

도메인의 엔터티를 강력하게 입력하여 다음을 수행할 수 있습니다.

  1. 귀중한 서버 CPU 주기를 소모하는 런타임 시 수행해야 하는 검사의 수를 줄이십시오(매우 적은 양이지만 분당 수천 개의 요청을 처리할 때 합산됩니다).
  2. TypeScript 컴파일러가 제공하는 보장으로 인해 더 적은 수의 기본 테스트를 유지합니다.
  3. 편집기 및 컴파일러 지원 리팩토링을 활용하십시오.
  4. 향상된 신호 대 잡음비를 통해 코드 가독성을 향상시킵니다.

단점

유형 모델링에는 고려해야 할 몇 가지 절충안이 있습니다.

  1. TypeScript를 도입하면 일반적으로 도구 체인이 복잡해져 빌드 및 테스트 제품군 실행 시간이 길어집니다.
  2. 목표가 기능을 프로토타입화하여 최대한 빨리 사용자에게 제공하는 것이라면 유형을 명시적으로 모델링하고 코드베이스를 통해 전파하는 데 필요한 추가 노력은 가치가 없을 수 있습니다.

우리는 서버 또는 공유 백엔드/프론트엔드 유효성 검사 계층의 기존 JavaScript 코드를 유형으로 확장하여 코드 가독성을 개선하고 팀의 중요한 요구 사항인 더 안전한 리팩토링을 허용하는 방법을 보여주었습니다.

선언적 사용자 인터페이스

선언적 프로그래밍 기술을 사용하여 개발된 사용자 인터페이스는 "어떻게"보다 "무엇"을 설명하는 데 집중합니다. 웹의 세 가지 주요 기본 구성 요소인 CSS와 HTML은 시간과 10억 개 이상의 웹 사이트에 대한 테스트를 거친 선언적 프로그래밍 언어입니다.

웹을 구동하는 주요 언어
웹을 구동하는 주요 언어.

React는 2013년 Facebook에서 오픈 소스로 제공되었으며 프론트 엔드 개발 과정을 크게 변경했습니다. 처음 사용했을 때 GUI를 응용 프로그램 상태의 함수로 선언 하는 방법이 마음에 들었습니다. 이제 DOM 조작의 지저분한 세부 사항을 처리하고 사용자 작업에 대한 응답으로 업데이트해야 하는 앱 부분을 추적하지 않고도 더 작은 구성 요소에서 크고 복잡한 UI를 구성할 수 있었습니다. UI를 정의할 때 시간 측면을 크게 무시하고 애플리케이션이 한 상태에서 다음 상태로 올바르게 전환되도록 하는 데 집중할 수 있습니다.

프론트엔드 자바스크립트의 진화
프론트엔드 자바스크립트의 발전 방법 에서 무엇 으로 .

UI를 개발하는 더 간단한 방법을 달성하기 위해 React는 개발자와 기계/브라우저 사이에 추상화 계층 인 가상 DOM 을 삽입했습니다.

다른 최신 웹 UI 프레임워크도 이 격차를 해소했습니다. 예를 들어 Vue는 JavaScript getter/setter(Vue 2) 또는 프록시(Vue 3)를 통해 기능적 반응성을 사용합니다. Svelte는 추가 소스 코드 컴파일 단계(Svelte)를 통해 반응성을 제공합니다.

이러한 예는 개발자가 선언적 접근 방식을 통해 애플리케이션 동작을 표현할 수 있는 더 우수하고 간단한 도구를 제공하려는 업계의 열망을 보여주는 것 같습니다.

선언적 애플리케이션 상태 및 논리

프리젠테이션 레이어가 계속해서 일부 HTML 형식(예: React의 JSX, Vue, Angular 및 Svelte에서 발견되는 HTML 기반 템플릿)을 중심으로 회전하는 동안, 저는 다음과 같은 방식으로 애플리케이션의 상태를 모델링하는 방법의 문제를 가정합니다. 다른 개발자가 쉽게 이해할 수 있고 응용 프로그램이 성장함에 따라 유지 관리할 수 있는 문제는 아직 해결되지 않았습니다. 우리는 오늘날까지 계속되는 국가 관리 라이브러리 및 접근 방식의 확산을 통해 이에 대한 증거를 봅니다.

최신 웹 앱에 대한 기대치가 높아지면서 상황이 복잡해졌습니다. 현대 국가 관리 접근 방식이 지원해야 하는 몇 가지 새로운 도전 과제:

  • 고급 구독 및 캐싱 기술을 사용하는 오프라인 우선 애플리케이션
  • 계속 축소되는 번들 크기 요구 사항에 대한 간결한 코드 및 코드 재사용
  • 고화질 애니메이션 및 실시간 업데이트를 통해 점점 더 정교해지는 사용자 경험 요구

유한 상태 기계 및 상태 차트의 (재) 출현

유한 상태 기계는 항공 및 금융과 같이 응용 프로그램의 견고성이 중요한 특정 산업에서 소프트웨어 개발에 광범위하게 사용되었습니다. 또한 예를 들어 우수한 XState 라이브러리를 통해 웹 앱의 프론트 엔드 개발을 위해 꾸준히 인기를 얻고 있습니다.

Wikipedia는 유한 상태 기계를 다음과 같이 정의합니다.

주어진 시간에 유한한 수의 상태 중 정확히 하나에 있을 수 있는 추상 기계. FSM은 일부 외부 입력에 대한 응답으로 한 상태에서 다른 상태로 변경할 수 있습니다. 한 상태에서 다른 상태로의 변화를 전환이라고 합니다. FSM은 상태 목록, 초기 상태 및 각 전환에 대한 조건으로 정의됩니다.

그리고 더:

상태는 전환을 실행하기 위해 대기 중인 시스템의 상태에 대한 설명입니다.

기본 형태의 FSM은 상태 폭발 문제로 인해 대규모 시스템으로 확장되지 않습니다. 최근 UML 상태 차트는 FSM을 계층 구조와 동시성을 통해 확장하기 위해 만들어졌으며, 이는 상용 응용 프로그램에서 FSM을 광범위하게 사용할 수 있게 해줍니다.

애플리케이션 로직 선언

첫째, FSM은 코드로 어떻게 생겼습니까? JavaScript에서 유한 상태 기계를 구현하는 방법에는 여러 가지가 있습니다.

  • switch 문으로서의 유한 상태 기계

다음은 switch 문을 사용하여 구현된 JavaScript가 있을 수 있는 상태를 설명하는 기계입니다.

 const initialState = { type: 'idle', error: undefined, result: undefined }; function transition(state = initialState, action) { switch (action) { case 'invoke': return { type: 'pending' }; case 'resolve': return { type: 'completed', result: action.value }; case 'error': return { type: 'completed', error: action.error ; default: return state; } }

이 스타일의 코드는 인기 있는 Redux 상태 관리 라이브러리를 사용해 본 개발자에게 친숙할 것입니다.

  • JavaScript 객체로서의 유한 상태 기계

다음은 JavaScript XState 라이브러리를 사용하여 JavaScript 객체로 구현된 동일한 시스템입니다.

 const promiseMachine = Machine({ id: "promise", initial: "idle", context: { result: undefined, error: undefined, }, states: { idle: { on: { INVOKE: "pending", }, }, pending: { on: { RESOLVE: "success", REJECT: "failure", }, }, success: { type: "final", actions: assign({ result: (context, event) => event.data, }), }, failure: { type: "final", actions: assign({ error: (context, event) => event.data, }), }, }, });

XState 버전은 덜 컴팩트하지만 객체 표현에는 몇 가지 장점이 있습니다.

  1. 상태 머신 자체는 쉽게 지속할 수 있는 간단한 JSON입니다.
  2. 선언적이기 때문에 기계를 시각화할 수 있습니다.
  3. TypeScript를 사용하는 경우 컴파일러는 유효한 상태 전환만 수행되는지 확인합니다.

XState는 상태 차트를 지원하고 SCXML 사양을 구현하므로 초대형 애플리케이션에서 사용하기에 적합합니다.

약속의 상태 차트 시각화:

약속의 유한 상태 기계
약속의 유한 상태 기계.

XState 모범 사례

다음은 프로젝트를 유지 관리하는 데 도움이 되도록 XState를 사용할 때 적용할 몇 가지 모범 사례입니다.

논리에서 부작용 분리

XState를 사용하면 상태 머신의 논리와 독립적으로 지정되는 부작용(로깅 또는 API 요청과 같은 활동 포함)을 허용합니다.

다음과 같은 이점이 있습니다.

  1. 상태 기계 코드를 가능한 한 깨끗하고 단순하게 유지하여 논리 오류 감지를 지원합니다.
  2. 추가 상용구를 먼저 제거하지 않고도 상태 머신을 쉽게 시각화할 수 있습니다.
  3. 모의 서비스를 주입하여 상태 머신을 더 쉽게 테스트할 수 있습니다.
 const fetchUsersMachine = Machine({ id: "fetchUsers", initial: "idle", context: { users: undefined, error: undefined, nextPage: 0, }, states: { idle: { on: { FETCH: "fetching", }, }, fetching: { invoke: { src: (context) => fetch(`url/to/users?page=${context.nextPage}`).then((response) => response.json() ), onDone: { target: "success", actions: assign({ users: (context, event) => [...context.users, ...event.data], // Data holds the newly fetched users nextPage: (context) => context.nextPage + 1, }), }, onError: { target: "failure", error: (_, event) => event.data, // Data holds the error }, }, }, // success state.. // failure state.. }, });

작업을 계속하는 동안 이런 방식으로 상태 머신을 작성하고 싶은 유혹이 있지만 부작용을 옵션으로 전달하면 문제를 더 잘 분리할 수 있습니다.

 const services = { getUsers: (context) => fetch( `url/to/users?page=${context.nextPage}` ).then((response) => response.json()) } const fetchUsersMachine = Machine({ ... states: { ... fetching: { invoke: { // Invoke the side effect at key: 'getUsers' in the supplied services object. src: 'getUsers', } on: { RESOLVE: "success", REJECT: "failure", }, }, ... }, // Supply the side effects to be executed on state transitions. { services } });

이것은 또한 상태 머신의 쉬운 단위 테스트를 허용하여 사용자 가져오기의 명시적 조롱을 허용합니다.

 async function testFetchUsers() { return [{ name: "Peter", location: "New Zealand" }]; } const machine = fetchUsersMachine.withConfig({ services: { getUsers: (context) => testFetchUsers(), }, });

대형 기계 분할

시작할 때 문제 영역을 좋은 유한 상태 기계 계층 구조로 가장 잘 구조화하는 방법이 항상 즉시 명확한 것은 아닙니다.

팁: UI 구성 요소의 계층 구조를 사용하여 이 프로세스를 안내하세요. 상태 머신을 UI 구성 요소에 매핑하는 방법에 대한 다음 섹션을 참조하세요.

상태 머신을 사용하는 주요 이점은 애플리케이션의 모든 상태와 상태 간 전환을 명시적으로 모델링하여 결과 동작을 명확하게 이해하여 논리 오류 또는 격차를 쉽게 찾아낼 수 있다는 것입니다.

이것이 잘 작동하려면 기계를 작고 간결하게 유지해야 합니다. 다행히도 상태 머신을 계층적으로 구성하는 것은 쉽습니다. 신호등 시스템의 표준 상태 차트 예에서 "빨간색" 상태 자체가 하위 상태 머신이 됩니다. 부모 "빛" 머신은 "빨간색"의 내부 상태를 인식하지 못하지만 "빨간색"에 들어갈 때와 나갈 때 의도한 동작을 결정합니다.

상태 차트를 사용한 신호등 예제
상태 차트를 사용한 신호등 예제.

1-1 Stateful UI 구성 요소에 대한 상태 머신 매핑

예를 들어 다음과 같은 React 보기가 있는 훨씬 단순화된 가상의 전자 상거래 사이트를 살펴보겠습니다.

 <App> <SigninForm /> <RegistrationForm /> <Products /> <Cart /> <Admin> <Users /> <Products /> </Admin> </App>

위의 보기에 해당하는 상태 머신을 생성하는 프로세스는 Redux 상태 관리 라이브러리를 사용해 본 사람들에게 친숙할 수 있습니다.

  1. 구성 요소에 모델링해야 하는 상태가 있습니까? 예를 들어, Admin/Products는 그렇지 않을 수 있습니다. 서버에 대한 페이징 페치와 캐싱 솔루션(예: SWR)으로 충분할 수 있습니다. 반면에 SignInForm이나 Cart와 같은 컴포넌트는 일반적으로 필드에 입력된 데이터나 현재 카트 내용과 같이 관리해야 하는 상태를 포함합니다.
  2. 로컬 상태 기술(예: React의 setState() / useState() )이 문제를 포착하기에 충분합니까? 카트 팝업 모달이 현재 열려 있는지 여부를 추적하려면 유한 상태 기계를 사용할 필요가 거의 없습니다.
  3. 결과 상태 머신이 너무 복잡할 가능성이 있습니까? 그렇다면 다른 곳에서 재사용할 수 있는 하위 시스템을 만들 기회를 식별하여 시스템을 더 작은 여러 개로 분할합니다. 예를 들어, SignInForm 및 RegistrationForm 시스템은 자식 textFieldMachine의 인스턴스를 호출하여 사용자 이메일, 이름 및 암호 필드에 대한 유효성 및 상태를 모델링할 수 있습니다.

유한 상태 기계 모델을 사용하는 경우

상태 차트와 FSM은 몇 가지 까다로운 문제를 우아하게 해결할 수 있지만 특정 응용 프로그램에 사용할 최상의 도구와 접근 방식을 결정하는 것은 일반적으로 여러 요인에 따라 다릅니다.

유한 상태 기계를 사용하는 몇 가지 상황은 다음과 같습니다.

  • 애플리케이션에는 필드 접근성 또는 가시성이 복잡한 규칙(예: 보험 청구 앱의 양식 입력)에 의해 관리되는 상당한 데이터 입력 구성 요소 가 포함되어 있습니다. 여기에서 FSM은 비즈니스 규칙이 강력하게 구현되도록 돕습니다. 또한 상태 차트의 시각화 기능을 사용하여 비기술적 이해 관계자와의 협업을 늘리고 개발 초기에 상세한 비즈니스 요구 사항을 식별할 수 있습니다.
  • 느린 연결에서 더 잘 작동하고 사용자에게 더 높은 충실도의 경험을 제공하려면 웹 앱은 점점 더 복잡해지는 비동기 데이터 흐름을 관리해야 합니다. FSM은 애플리케이션이 있을 수 있는 모든 상태를 명시적으로 모델링하고 상태 차트를 시각화하여 비동기식 데이터 문제를 진단하고 해결할 수 있습니다.
  • 정교한 상태 기반 애니메이션이 많이 필요한 응용 프로그램. 복잡한 애니메이션의 경우 RxJS를 사용하여 시간이 지남에 따라 이벤트 스트림으로 애니메이션을 모델링하는 기술이 널리 사용됩니다. 많은 시나리오에서 이것은 잘 작동하지만 풍부한 애니메이션이 알려진 일련의 복잡한 상태와 결합될 때 FSM은 애니메이션이 흐르는 잘 정의된 "휴식 지점"을 제공합니다. RxJS와 결합된 FSM은 충실도가 높고 표현력이 뛰어난 사용자 경험의 차세대 물결을 제공하는 데 도움이 되는 완벽한 조합으로 보입니다.
  • 사진 또는 비디오 편집, 다이어그램 생성 도구 또는 많은 비즈니스 로직이 클라이언트 측에 있는 게임과 같은 리치 클라이언트 애플리케이션 . FSM은 본질적으로 UI 프레임워크 또는 라이브러리와 분리되어 있으며 고품질 애플리케이션을 신속하게 반복하고 자신 있게 배송할 수 있도록 테스트를 작성하기 쉽습니다.

유한 상태 기계 주의 사항

  • XState와 같은 상태 차트 라이브러리에 대한 일반적인 접근 방식, 모범 사례 및 API는 특히 경험이 부족한 팀의 경우 생산성을 높이기 위해 시간과 리소스 투자가 필요한 대부분의 프론트 엔드 개발자에게 새로운 것입니다.
  • 이전 경고와 유사하게 XState의 인기가 계속 증가하고 문서화되어 있지만 Redux, MobX 또는 React Context와 같은 기존 상태 관리 라이브러리에는 XState가 아직 일치하지 않는 풍부한 온라인 정보를 제공하는 엄청난 추종자들이 있습니다.
  • 더 간단한 CRUD 모델을 따르는 애플리케이션의 경우 SWR 또는 React Query와 같은 우수한 리소스 캐싱 라이브러리와 결합된 기존 상태 관리 기술로 충분합니다. 여기서 FSM이 제공하는 추가 제약 조건은 복잡한 앱에 매우 유용하지만 개발 속도를 늦출 수 있습니다.
  • 도구는 개선된 TypeScript 지원 및 브라우저 devtools 확장에 대한 작업이 여전히 진행 중인 다른 상태 관리 라이브러리보다 덜 성숙합니다.

마무리

웹 개발 커뮤니티에서 선언적 프로그래밍의 인기와 채택은 계속해서 증가하고 있습니다.

현대적인 웹 개발은 계속해서 더 복잡해지고 있지만 선언적 프로그래밍 접근 방식을 채택하는 라이브러리와 프레임워크는 점점 더 자주 등장합니다. 그 이유는 분명해 보입니다. 소프트웨어 작성에 대한 더 간단하고 설명적인 접근 방식이 필요하기 때문입니다.

TypeScript와 같은 강력한 형식의 언어를 사용하면 응용 프로그램 도메인의 엔터티를 간결하고 명시적으로 모델링할 수 있으므로 오류 가능성과 조작해야 하는 오류가 발생하기 쉬운 검사 코드의 양이 줄어듭니다. 프론트 엔드에 유한 상태 머신과 상태 차트를 채택하면 개발자가 상태 전환을 통해 애플리케이션의 비즈니스 로직을 선언할 수 있으므로 풍부한 시각화 도구를 개발할 수 있고 개발자가 아닌 사람들과 긴밀한 협업 기회를 늘릴 수 있습니다.

이를 수행할 때 애플리케이션 작동 방식에 대한 기본 개념에서 벗어나 고객의 요구 사항에 더욱 집중하고 지속적인 가치를 창출할 수 있는 상위 수준 보기로 초점을 이동합니다.