@ 상태란?
응용 프로그램 상태는 응용 프로그램의 전체 메모리입니다.
간단히 말해서 애플리케이션 상태는 API 호출, 사용자 입력, 프리젠 테이션 UI 상태, 애플리케이션 환경 설정 등으로 수신 된 데이터로 구성됩니다.
애플리케이션 상태의 단순하고 구체적인 예는 CRM 애플리케이션에서 유지 보수되는 고객 목록
Angular 응용 프로그램은 일반적으로 많은 구성 요소로 구성됩니다. 이러한 각 구성 요소는 자체 상태를 가지며 다른 구성 요소의 상태를 인식하지 못합니다.
부모 - 자식 구성 요소 간의 정보를 공유하기 위해, 우리는 사용 @Input
하고 @Output
장식. 그러나 이 방법은 응용 프로그램이 아래와 같이 몇 가지 구성 요소로 구성된 경우에만 실용적
부품 개수가 증가 할 때, 단독 통해 구성 요소간에 정보를 전달하는 악몽된다 @Input
및 @Output
장식.
범용 컴포넌트를 만드는 것도 아니고, 보통 해당 앱에서만 사용되는 Component일텐데, 굳이 @Input @Output처럼 정형화된 함수 형태의 Component를 만들 것이 아니라, 만약 특정 Model의 data를 표시하는 Component라면, Store라는 하나의 중앙관리 시스템에서 필요한 data를 가장 작은 부품 컴포넌트라도 받아와서 보여주는 것이다.
구성 요소에서 구성 요소로가 아니라 상점과 구성 요소간에 데이터가 흐릅니다.
@ AKita
RxJS 위에 구축되었으며, Flux 및 Redux와 같은 모델에서 영감(하기 설명된 것이 Redux이므로, 동작원리는 동일하다고 보면된다.)
Akita의 가장 매력적인 기능은 단순성입니다.
애플리케이션 상태를 관리하기 위해 상용구 코드를 작성하는 번거 로움을 덜어줍니다.
데이터흐름을 단방향으로 바꾸기 위해, Akita가 하는 역할은 크게 저장(Store)과 질문(Query)입니다.
- Akita의 Store
상점 상태를 포함하고 "단일 진실 소스"역할을하는 단일 오브젝트
EntityStore: 실제 응용 프로그램에서는 일반적으로 애쉬 제품, 코스, 사용자 등의 엔티티와 함께 작업합니다.
엔티티와의 작업 프로세스를 단순화하기 위해 Akita는보다 구체적인 Store을 소개했습니다
EntityStore. 대부분의 경우 애플리케이션에 필요한 상점(Store)은 엔티티 상점(Store)
엔터티 저장소를 데이터베이스의 테이블로 생각할 수 있습니다. 여기서 각 테이블은 플랫 엔터티 모음을 나타냅니다. 다음 코드는 개체 EntityStore관리 방법
애플리케이션에는 여러 엔티티 저장소가있어 다른 엔티티를 관리 할 수 있습니다.
-> 대부분은 Entity별로 하나의 Store.ts 객체를 생성한다고 생각
엔티티 저장소는 상태를 업데이트하는 데 사용할 수있는 메소드 세트를 제공합니다.
<OOO.store.ts에서 제공하는 메소드, 각 Service 들에서 호출하여 사용>
-> set () : 현재 컬렉션을 제공된 컬렉션으로 교체하고 활성 엔터티를 재설정합니다(전체 교체)
-> add () : 상점에 엔티티를 추가하십시오
-> update () : 상점의 엔티티 업데이트
-> upsert () : 엔터티를 삽입하거나 업데이트합니다. id; 과 일치하는 엔티티가 없을 때 새 엔티티를 작성합니다 . 그렇지 않으면 업데이트를 수행(해당 Store와 관련된 Entity 업데이트)
-> remove () : 상점에서 하나 이상의 엔티티를 제거합니다
합니다.
이러한 메소드는 일반적으로 Angular 서비스[Redux의 Reducer와 완전 동일, Reducer함수처럼 이전 상태와 사용자액션객체를 전달받는다.] 내에서 set(), upsert(), remove()를 호출 하는 것이 원칙
- AKita의 Query
Query는 Store를 조회 할 수있는 클래스입니다. 쿼리는 데이터베이스 쿼리와 유사하다고 생각할 수 있습니다.
이 constructor함수(생성자)는 자체 저장소[메인 Store와 관련된 Entity.store.ts] 및 선택적으로 다른 쿼리 클래스에 대한 참조를받습니다. 일반적인 쿼리 클래스는 다음과 같습니다.
-> 즉, User.Store.ts가 있고, User에 대한 User.Query.ts가 있다면, User.Query.ts 내에서는 User.store.ts와 Book.query.ts 등을 참조한다.
-> Entity(Store) 전용 Query.ts가 있고, 특정 기능을 더 잘 수행하기 위해, 특정 Query.ts를 생성할 수 있다.
Query엔터티 : 엔터티 Query는 일반 Query기능과 비슷 하며 추가 기능이 맞춤형입니다 EntityStore. 다음 코드는 Query엔터티를 사용하여 ToDo엔터티 저장소 를 쿼리 하는 방법을 나타냅니다 .
QueryEntity상점에서 데이터를 검색하는 일련의 메소드 다음은 이러한 방법 중 일부입니다.
<OOO.query.ts 상에서 제공하는 메소드, 해당 query 객체 또는 타 query 객체에서 호출하여 사용>
-> selectAll () : 전체 상점의 엔티티 콜렉션을 선택하십시오.
-> selectMany () : 상점에서 여러 엔티티를 선택하십시오 .
-> selectEntity () : 엔터티 또는 엔터티 조각을 선택합니다 //findOne
@ 일반적인 상태관리 App 구현하기
1. Rest 방식에 따라 Backend API 구현
// Retrieve all courses
GET http://localhost:8080/api/courses
// Create a course
POST http://localhost:8080/api/courses
// Delete a course
DELETE http://localhost:8080/api/courses/{courseId}
// Update a course
PUT http://localhost:8080/api/courses/{courseId}
2. 관련된 Module 생성
ng generate module course
3. Entity를 나타내는 model interface 정의
Course엔티티 를 나타내는 모델 인터페이스를 정의해야합니다 .
라는 파일 course.model.ts을 만들어 app/course/model폴더 아래(shared 구조를 원한다면, global한 _model 폴더 내에 모아서 구성해도 상관없음
4-1. 관련된 Entity course.store [상태 관리 로직을 적용하기 위해 기존에서 추가되는 부분]
엔터티 store및 쿼리 엔터티와 같은 Akita 아티팩트를 정의해야합니다 .
이 아티팩트는 디렉토리 store아래에있는 app/course디렉토리 내에 작성
course.query.ts 내에 가장 기본적인 것은
- 기본 엔티티 상태를 재정의
- name을 사용하여 추가 속성을 정의
(ex). areCoursesLoaded. 코스 엔터티가 상태에 이미 저장되어 있는지 확인하는 데 사용되는 부울 플래그
추가속성을 사용하게 되면, 업데이트시, ... 또는 Object.assign으로 entity외에 객체 병합이 필요하다.
일반적으로 Angular 응용 프로그램은 REST API와 상호 작용하여 데이터에 대한 CRUD 작업을 수행
loadCourses(courses: Course[], areCoursesLoaded: boolean) {
this.set(courses); //store 내에 courses 객체 덮어씌우기
this.update(state => ({
...state,
areCoursesLoaded
})); //이전 상태에 areCoursesLoaded : true or false 인 값이 추가된 객체로 업데이트
}
4-2. 관련된 Entity course.query 작성 [상태 관리 로직을 적용하기 위해 기존에서 추가되는 부분]
직접 DB를 조회하는 것이 아니라, 이미 조회해온, Store를 쿼리하는 것?
★ 원래는 Store.ts만 존재하고, View는 Store를 직접 참조해서, 필요한 값을 찾아오는 건데, Akita는 Store에서 조회 부분을 query.ts로 추출해서, View는 대부분 query.ts(읽기 전용이기 때문에 가능한 듯)를 참조하여 필요한 화면을 구성한다.
★ 도대체 Store는 언제, RestApi로 값을 가져오는 거지?[값이 변경되는 모든 건 Service가 담당, 나머지는 다 읽기전용]
5. 관련된 Entity service.ts 정의
서비스는 REST API와 상호 작용하고 데이터 작업을 수행하는 데 사용됩니다. 서비스 클래스를 정의하려면 이름이 지정된 파일을 작성 course.service.ts하여 app/course/services폴더 아래에 배치하십시오 . 이 서비스 클래스는 두 가지 주요 작업을 담당합니다.
- 1. 백엔드 API를 호출하고 데이터 조작을 수행합니다.(DB 데이터 변경, API를 사용하여, DB 데이터 조회)
- 2. Akita Entity.Store.ts에서 제공하는 기능 (set, upsert, remove) [Store를 참조]으로 응용 프로그램 상태 업데이트(여기서 Store값을 업데이트)
서비스 클래스가 정의되면 course.module.ts아래와 같이 파일에 서비스 클래스를 등록
6. 컴포넌트 정의[리스트와 디테일]
FormsModule및 HttpClientModule있는 각각 템플릿 및 서비스에 의해 사용,
리스트 컴포넌트에서는 주로 query.ts를 사용
디테일 컴포넌트에서는 주로 query.ts와 service를 사용
7. 예제
https://github.com/sarindufit/angular-akita-example
@ 기본 Web App
MVC 구조 : Model - View - Controller 로써, 양방향 바인딩이 많이 이루어진다.
Model(Data)의 값이 변경되면, View도 변경 되고,
View가 변경되면, Model(Data)도 변경된다.
이와 같은 방식은, View와 연관된 Model이 하나 혹은 적은 수일 경우에는 유용하다.
하나의 View와 연관된, Model이 많아질수록, 데이터 흐름이 복잡하게 얽혀서...
한 개의 모델이 여러 개의 뷰를 조작하고 한 개의 뷰가 여러 개의 모델을 조작한다면 데이터 흐름을 이해하기 힘들어진다.
@ 상태 관리의 개념
Model이 View를 View가 Model을 변경할 수 있게 하는 양방향 바인딩을 버리고, 단방향 데이터 흐름으로 바꾸는 발상의 전환이다.
간단한 경우, View가 바로 Model을 변경시키고, Model이 바로 View를 변경시키는 것이 별도의 파일없이, 훨씬 편하다. 그러나, 앱이 복잡해질수록, 위와 같은 방식은 유지가 어려워진다.
초기 상태관리의 개념을 적용한다면,
View는 MVC 패턴과 달리 데이터를 변경시키지 않고 Action을 넘겨준다.
Action은 반드시 Dispatcher를 지나게 되고
Dispatcher를 통해서 데이터(기존 Model) 변경이 일어나고
View는 변경된 데이터를 Store를 통해서 전달 받는다.
이러한 단방향 데이터 흐름은 기존의 MVC 패턴에 있던 ‘상태의 전이’(뷰와 모델 사이의 데이터 변경이 연결된 수많은 곳으로 따라 변경되는 현상) 현상을 없애주고 ‘예측 가능하다’라는 장점이 존재한다.
1. 가장 먼저, 각 View는 관련된 Store등(여러개)로부터 데이터를 받아 화면을 보여주도록 정의되어 있다.
왼쪽은 View의 계층구조이다.
2. View의 자식, 부모 어디서든지 데이터를 변경시킬 만한 동작이 이루어졌다면, 기존에는 관련된 여러개의 Model(data.service.ts와 같이?)에 직접 접근하여, 값을 변경했을 것이다.
3.그러나 단방향 데이터 흐름을 가진다면, View는 직접 Model에 접근하는 것이 아니라, 어떠한 행동을 했는지만 정의해서, Reducer에 보내준다.
4. Reducer는 미리 정의된 사용자 행위에 따라, 관련된 Store(기존 Model과 비슷, 여러개)의 값을 변경한다.
5. Store의 상태가 변화되면 Store는 관련되어 있던 View들에게, 상태변화 Event를 보내준다.
@ 상태관리의 원칙
1. 진실은 하나의 소스로부터📁
애플리케이션의 모든 상태는 하나의 스토어 안에 하나의 객체 트리 구조로 저장됩니다.
하나의 Store를 가지는 것을 권장(기존 Model처럼 여러개의 Model이 존재하는 것이 아니라, 기존 Model은 하나의 Store 안의 객체 트리구조로 구성)
리액트에서는 부모 컴포넌트가 자식 컴포넌트에서 데이터(상태)를 넘겨줄 수 있지만 이 데이터의 원천은 항상 스토어가 된다.
모든 변경 사항의 Store에 기록되기 때문에 데이터 흐름의 원천은 항상 ‘Store’이어야 한다.
상황과 필요에 따라 스토어를 여러개 만들 수도 있다.
너무 많은 스토어를 가진 Redux라면 MVC 패턴과 큰 차이가 있는지 생각해아함
2. 상태는 읽기 전용이다
상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법
기존에는 직접 변경시켰지만, 이제는 사용자가 어떤 행위를 했는지에 따라서, 미리 정의된 로직에 따라서만 데이터가 변경된다.
View에서 일어나는 이벤트는 직접 데이터(상태)를 변경해서는 안 된다. 이벤트는 Action을 Reducer로 전달할 뿐이다. 데이터의 변경은 Reducer만 할 수 있다. Reducer 이외의 공간에서는 데이터(상태)는 읽기모드
모든 상태 변화는 중앙에서 관리되며 모든 액션은 엄격한 순서에 의해 하나하나 실행되기 때문에, 신경써서 관리해야할 미묘한 경쟁 상태는 없습니다. 액션은 그저 평범한 객체입니다. 따라서 기록을 남길 수 있고, 시리얼라이즈할 수 있으며, 저장할 수 있고, 이후에 테스트나 디버깅을 위해서 재현하는 것도 가능
3. 상태 변화(Reducer 내에서 이루어짐)는 순수 함수로 작성되어야한다
액션에 의해 상태 트리가 어떻게 변화하는 지를 지정하기 위해 프로그래머는 순수 리듀서를 작성해야한다.[Reducer 내에]
순수함수란?
함수가 실행되는 곳이 어디서든, 언제든 외부의 상태를 변경하지 않으면서 동일한 입력값에는 동일한 결과값을 반환(함수안에, global or static변수와 같이 상태를 저장하면 안된다.)
순수 Reducer는 2가지 특징을 더 갖는다.
- Reducer는 반드시 이전의 데이터(상태)와 Action을 매개변수(입력값)로 받는다.
- Reducer는 결과값으로 이전의 데이터를 변경시키지 않고 새로운 데이터(객체)를 만들어 반환한다.
@ 출처
- https://medium.com/@wooder2050/%EB%A6%AC%EB%8D%95%EC%8A%A4-redux-%EB%8A%94-%EC%99%9C-%EC%93%B0%EB%8A%94-%EA%B1%B4%EB%8D%B0-2eaafce30f27
'[DEV] App Dev ∕ Web Front > Framework ∕ Angular' 카테고리의 다른 글
ExpressionChangedAfterItHasBeenCheckedError 에러에 대하여 (0) | 2020.11.11 |
---|---|
Angular Akita 기본 구조 (0) | 2020.10.30 |
NestJs+TypeOrm B+ 앱 만들기 (0) | 2020.10.12 |
기초 Tip 기록 (0) | 2020.09.21 |
Service에 대하여 (0) | 2020.09.04 |
최근댓글