그래서 VPC가 뭔데 라고 한다면 일단
Amazon Virtual Private Cloud(VPC)는 기본 구성 요소로서, 이를 통해 AWS 클라우드 내에서 논리적으로 격리된 가상 네트워크를 프로비저닝할 수 있습니다. 자체 VPC를 생성하면 네트워킹 환경을 완전히 제어할 수 있습니다. 예를 들어, IP 주소 범위, 서브넷, 라우팅 테이블 및 연결 옵션을 정의할 수 있습니다.

출처 : https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/configure-your-vpc.htm

위와 같이 여러 리전(지역)안에 vpc를 만들고 그 안에 AZ(Availability Zone) 안에 또 subnet을 나눠서 네트워크를 분리하는 것이다.

 

VPC를 이해하려면 기본적으로 IP에 대해서 알아야 하는것 같다.

 

IP(Internet Protocol)

IP는 인터넷이 통하는 네트워크에서 어떤 정보를 수신하고 송신하는 통신에 대한 규약이다.

 

aws를 이해하는데는 어떤 ip인가의 그런 의미 보다는 대충 ip를 읽을줄 알아야 할것 같다.

이유는 IP 주소로 VPC내의 리소스가 서로 통신하고, 인터넷을 통해 다른 리소스와 통신하기 때문이다.

 

특히 알아야할것 같은 부분은 CIDR(classless inter-domain  routing) 표기법인데 이걸 통해서 ip 주소 및 네트워크 마스크를 하기 때문이다.

 

대충 표시되는 법은

- 개별 IPv4 주소는 32비트로, 최대 3개의 십진수로 구성된 그룹 4개를 포함합니다. 예를 들어 10.0.1.0입니다.
- IPv4 CIDR 블록에는 마침표로 구분된 0~255의 십진수가 최대 3개인 4개 그룹이 있으며, 그 뒤에 슬래시와 0~32의 숫자가 표시됩니다. 예: 10.0.0.0/16.
- 개별 IPv6 주소는 128비트로, 4개의 16진수로 구성된 그룹 8개를 포함합니다. 예: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.
- IPv6 CIDR 블록에는 콜론으로 구분된 최대 4개의 16진수로 구성된 4개 그룹이 있으며, 그 뒤에 이중 콜론, 슬래시, 1~128의 숫자가 차례로 따라옵니다. 예: 2001:db8:1234:1a00::/56.

출처 : https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/vpc-ip-addressing.html

와 같이 표시하며 subnet을 쓰기 위해서는 위의 정도는 알아야 할 것 같다.

 

그래서 VPC를 어떻게 사용해야 하나

위와 같이 구성하면 된다.

즉 vpc /16 의 가장 큰 대역으로 설정한 것이며 그 안에 subnet 항목으로 subnet/24 대역으로 쪼갠것이다.

그래서 일단 vpc는 가상클라우드이고 어떤 ip 영역대역을 가지는 것이라는것을 대략적으로 알게 되었다. 그럼 subnet은 뭘까?

 

Subnet

우리가 집에 맛있는 케이크를 사왔는데 우리집은 아빠, 엄마, 나, 동생이므로 4조각으로 잘라서 먹기로 했다. 근데 엄마가 다이어트 중이라서 조금만 먹어도 된다고 해서 우리는 4등분을 먼저 한 다음 엄마가 받은 조각의 절반을 나한테 더 주었다. 이처럼 우리가 VPC로 할당 받은 ip 대역을 나누고, 그 나누어진 조각들을 subnet이라고 하면 된다.

 

예를 들어 만약 vpc를 설정후 subnet을 4개로 가져간다 하면

VPC : 10.0.0.0/16

subnet : 10.0.1.0/24(EC2 서버A), 10.0.2.0/24(EC2 서버B), 10.0.3.0/24(DB 서버A), 10.0.4.0/24(DB 서버B)

 

와 같이 4개로 쪼개고 그 안에 EC2 2개, DB 2개 처럼 쪼갤수 있다.

이제 실제로 VPC를 만들어 보고 그 안에 서브넷 4개를 만들어 보겠다.

 

VPC 만들기

vpc 탭에서 왼쪽 위의 vpc 생성을 눌러서 vpc 만들기를 진행한다.

 

안에 들어가 보면 vpc만 설정이랑 vpc등 설정이 있다. 기능이 많이 좋아진것 같다. vpc등으로 설정하면 알아서 subnet 설정, routing table 설정, 네트워크 설정 까지 한번에 해주는 기능인것 같다.

 

우리는 일단 vpc만 으로 설정한다.

vpc를 만들기에 앞서서 IPv4 CIDR을 입력해 줘야 하는데 최대 /16 최소 /28 의 대역을 설정할수 있다. 이는 나중에 자세하게 포스팅 해보려고 한다. 참고로 vpc를 만들기만 해서는 따로 비용이 안드므로(vpc로의 통신량을 가격으로 설정) 부담없이 만들면 된다.

그래서 우리는 10.0.0.0/16 으로 설정한다.

 

만들게 되면 

위와 같이 나의 vpc가 만들어 지게 된다.

 

그 다음에는 subnet을 만들면 된다.

Subnet 만들기

 

vpc 탭에 서브넷으로 들어가서 왼쪽 위의 서브넷 생성을 누른다.

 

 

난 위와 같이 만들었다. 

IPv4 VPC CIDR 블록에는 내가 만든 vpc를 넣었고

그 아래에는 10.0.0.0/20 약 4천개의 ip를 할당 받은 subnet으로 만들었다. 

그리고 그 아래 버튼을 보면 서브넷 추가 만들기 버튼이 있는데 이를 통해서 여러개를 만들수 있다. 

 

그렇게 나는 4개의 서브넷을 만들었고 각각 public 2개 private 2개로 만들것이다

 

이건 별게인데 cli로 서브넷이랑 vpc를 만들경우 아래와 코드가 같다.

# VPC 생성
aws ec2 create-vpc --cidr-block 10.0.0.0/16

# Public 서브넷 1
aws ec2 create-subnet --vpc-id vpc-xxxxxxxx --cidr-block 10.0.0.0/20 --availability-zone us-east-1a

# Public 서브넷 2
aws ec2 create-subnet --vpc-id vpc-xxxxxxxx --cidr-block 10.0.16.0/20 --availability-zone us-east-1b

# Private 서브넷 1
aws ec2 create-subnet --vpc-id vpc-xxxxxxxx --cidr-block 10.0.32.0/20 --availability-zone us-east-1a

# Private 서브넷 2
aws ec2 create-subnet --vpc-id vpc-xxxxxxxx --cidr-block 10.0.48.0/20 --availability-zone us-east-1b

 

어찌 저찌 VPN이 궁금해 졌는데 이유는 VPC가 뭐지? 에서 시작한다.

VPC(virtual private cloud) 이고 VPN(virtual private network)인데 차이가 뭐지?

가 궁금해서 알아보게 된것이다.

 

VPN이 뭔데 그래서

 

VPN은 한국어로 "가상 사설망"이라고 부른다.

우리는 보통 "VPN 우회" 등의 단오로 많이 접하게 된다.

출처 : https://hidemyacc.com/protect-yourself-with-vpn

 

vpn 네트워크에 접속하게 되면, 외부에 있는 컴퓨터라도 내부 네트워크에 접속해 있는것처럼 사용하는것이다.

 

예를 들면, A 컴퓨터(ip주소 : 200.xx.xx.xx)에서 vpn(ip주소 : 201.xx.xx.xx)로 접속한다고 가정을 해봅니다.

 

그럼 인터넷 입장에서는 A 컴퓨터는 200번이아닌 201번의 ip로 인식하게 되는겁니다.

 

만약, A 컴퓨터가 vpn을 통해서가 아닌 그냥 인터넷등을 접속하면 200번의 ip로 보여질 겁니다.

회사에서 만약 인바운드 규칙, 즉 특정 사이트를 들어올때 201.xx.xx.xx 의 ip만 허용할 경우 vpn을 통해서만 접속이 가능한 겁니다.

 

즉 vpn을 통할 경우 A 컴퓨터 -> vpn -> 인터넷 순으로 ip를 통하는 것이고 ip가 바뀜으로서 특정 사이트 또는 서버에 접속이 가능해지는것이다.

 

위처럼 특정 ip로의 접속 말고도 ip 를 분리할때도 사용한다.

 

출처 : https://medium.com/harrythegreat/aws-%EA%B0%80%EC%9E%A5%EC%89%BD%EA%B2%8C-vpc-%EA%B0%9C%EB%85%90%EC%9E%A1%EA%B8%B0-71eef95a7098

 

위처럼 우리 회사 네트워크가 구성되어 있다고 보자. 근데 특정 이유로 인해서 네트워크를 다시 분리하고 싶다면 기존 인터넷선 공사도 다시해야 되고 내부선을 다 고쳐야 한다 그럴때 내부 망을 사용하게 된다.

 

출처 : https://medium.com/harrythegreat/aws-%EA%B0%80%EC%9E%A5%EC%89%BD%EA%B2%8C-vpc-%EA%B0%9C%EB%85%90%EC%9E%A1%EA%B8%B0-71eef95a7098

 

실제로는 위와 동일 하지만 가상망을 만듬으로서 새로운 네트워크를 설치한것 같은 효과를 보는것이다.

 

VPN의 장점과 단점

장점

- 인터넷 사용 시 높은 보안과 프라이버시

- 공용 Wi-Fi 에서도 안전한 데이터 전송

- 지역 제한 콘텐츠에 접근 가능

- 원격으로 회사 네트워크에 안전하게 접속

 

단점

- 인터넷 속도가 느려질 수 이씅ㅁ

- 실뢰할 수 없는 VPN 서비스를 사용할 경우 데이터 유출 위험

- 비용 발생 

 

 

근무중 Trigger를 사용하게 될 일이 생겼는데 trigger 기능을 사용하려다 보니 trigger안에 sql을 직접 짜기 보다는 

stored function을 쓴다고 하는 글을 보게 되었다. 근데 나는 생각해 보니까 Procedure를 쓴다는 글도 본거 같아서 정리를 해보려고 한다.


Stored Function

CREATE FUNCTION 함수이름(파라미터이름 데이터타입, ...)
RETURNS 반환타입 AS $$
BEGIN
    -- 함수 로직
    RETURN 반환값;
END;
$$ LANGUAGE plpgsql;

 

CREATE FUNCTION 으로 다음에 함수 이름과 파라미터 목록을 작성

RETURNS 로 함수가 반환할 데이터 타입을 지정

LANGUAGE plpgsql 은 PostgreSQL의 절차적 언어인 PL/pgSQL을 사용한다는 의미이다.

 

간단한 예제로는 

CREATE FUNCTION is_adult(age INTEGER)
RETURNS BOOLEAN AS $$
BEGIN
    IF age >= 18 THEN
        RETURN TRUE;
    ELSE
        RETURN FALSE;
    END IF;
END;
$$ LANGUAGE plpgsql;

와 같이 다음의 나이를 받아 성인인지 여부를 반환한다.

 

CREATE FUNCTION get_users()
RETURNS TABLE(id INTEGER, name TEXT) AS $$
BEGIN
    RETURN QUERY SELECT id, name FROM users;
END;
$$ LANGUAGE plpgsql;

여러 행의 결과를 반환해야 할 때는 TABLE 키워드를 사용하여 테이블을 반환할 수 있다.

 

SELECT add_numbers(10, 20);  -- 결과: 30
SELECT is_adult(20);         -- 결과: TRUE
SELECT * FROM get_users();   -- 여러 행의 결과 반환

정의한 함수를 호출하려면 SELECT 문을 사용한다.

 


Procedure

procedure 인지 stored procedure인지는 정확하게 모르겠다.

기본 구문은 아래와 같다.

CREATE PROCEDURE 프로시저이름(파라미터이름 데이터타입 [IN | OUT | INOUT], ...)
LANGUAGE plpgsql
AS $$
BEGIN
    -- 프로시저 로직
END;
$$;

CREATE PROCEDURE 다음에 프로시저 이름과 파라미터 목록을 작성한다.

파라미터는 IN(입력), OUT(출력), INOUT(입출력)으로 지정이 가능하다.

 

간단한 예제로는

CREATE PROCEDURE add_employee(name TEXT, age INTEGER, department TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO employees (name, age, department) VALUES (name, age, department);
END;
$$;

새로운 직원을 추가하는 add_employee 프로시저이다.

 

또 다른 출력 파라미터를 사용하는 예제는

CREATE PROCEDURE sum_numbers(a INTEGER, b INTEGER, OUT result INTEGER)
LANGUAGE plpgsql
AS $$
BEGIN
    result := a + b;
END;
$$;

과 같이 두 수의 합을 축력하는 sum_numbers 프로시저도 있다.

프로시저 호출 방법은 CALL구문을 사용한다.

CALL add_employee('John Doe', 30, 'Engineering');

CALL sum_numbers(10, 20, result := 0);
SELECT result; -- 출력 파라미터로 값이 설정됨

그럼 두개의 차이는 무엇일까?

 

1. 반환 값

  • Stored Function: 특정 값을 반환합니다. 호출 시 반드시 하나의 값을 반환해야 하며, 주로 계산 결과나 특정 값을 가져오는 데 사용됩니다.
  • Stored Procedure: 값을 반환할 필요가 없으며, 여러 개의 값을 반환하거나 반환하지 않을 수 있습니다. 주로 데이터를 수정하는 작업(예: INSERT, UPDATE, DELETE)에 사용됩니다.

2. 호출 방식

  • Stored Function: SQL 구문에서 함수를 다른 구문처럼 사용할 수 있으며, SELECT 문에서 바로 호출하여 결과를 가져올 수 있습니다. 예: SELECT function_name(parameters);
  • Stored Procedure: 일반적으로 CALL 키워드를 사용하여 호출됩니다. 예: CALL procedure_name(parameters);

3. 사용 용도

  • Stored Function: 주로 값을 계산하거나 특정 데이터를 반환하는 용도로 사용됩니다. 로직을 SQL 내에 삽입해서 활용할 수 있습니다.
  • Stored Procedure: 데이터 조작, 여러 SQL 작업을 순차적으로 처리하는 데 유용합니다.

4. 파라미터 전송 방식

  • Stored Function: 입력 파라미터만 받을 수 있습니다.
  • Stored Procedure: 입력(IN), 출력(OUT), 입력 및 출력(INOUT) 파라미터를 모두 받을 수 있어 다방면으로 데이터 주고받기가 가능합니다.

5. SQL 문에서의 사용 가능성

  • Stored Function: 함수는 SQL 문의 일부로서 사용이 가능하여 WHERE, SELECT, ORDER BY 등 다양한 절에서 호출이 가능합니다.
  • Stored Procedure: 프로시저는 이러한 구문 내에서 직접 호출할 수 없습니다. 단독으로 호출되어야 합니다.

요약

  • Stored Function은 값을 반환하며, SQL 구문에 삽입하여 계산 목적으로 주로 사용됩니다.
  • Stored Procedure는 다양한 작업을 처리하며, 단독으로 호출되어 데이터 조작을 수행하는 데 더 적합합니다.

https://stackoverflow.com/questions/1179758/function-vs-stored-procedure-in-sql-server 에서 가져온 차이점

정말 간단하다..

 

 

 

ios > Podfile에서 위의

# platform :ios, '12.0'

의 부분을 주석 해제 해주면 된다. ( # 제거)

'FLUTTER' 카테고리의 다른 글

[Bloc] 개념 이해하기  (0) 2024.06.16
[Bloc] 개념 이해하기 2  (0) 2024.06.05
[Flutter] Bloc 개념 이해하기 1  (2) 2024.06.04
.framework does not support the minimum OS Version specified in the  (2) 2024.04.27
[Flutter] Gradle issue  (2) 2023.11.24

이전 글 : 2024.06.04 - [FLUTTER] - [Bloc] 개념 이해하기 2

 

Bloc

 

 

Bloc은 조금 더 나은 클래스로 events에 의존해서 state변경을 만듭니다.Bloc 은 또한 BlocBase를 extends하여 Cubit과 유사한 공통 Api가 있습니다.그러나 bloc은 새로운 state를 직접 emit하는 대신 event 를 수신하고 수신된 event를 나가는 state로 변환합니다.

 

Bloc 만들기

Bloc을 생성하는 것은 cubit을 생성하는 것과 비슷하지만, 관리할 state를 정의하는것 외에 bloc이 처리할 event도 정의해야 한다는 점이 다르다. event는 bloc에 대한 입력을 받는 것이다.

sealed class CounterEvent{}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extencds Bloc<CounterEvent, int> {
 CounterBloc() : super(0);
 }

 

Bloc의 state변화

Bloc 은 Cubit의 함수가 아닌 on<Event> Api를 통해 이벤트 핸들러를 등록해야 합니다. 이벤트 핸들러는 들어오는 모든 event를 0개 이상의 나가는 state로 변환하는 역활을 수행합니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      // 여기서 CounterIncrementPressed 를 구현한다.
    });
}

그런 다음 EventHandler를 업데이트 하여 CounterIncrementPressed 이벤트를 처리할 수 있다.

 

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      // 여기서 CounterIncrementPressed 를 구현한다.
      emit(state + 1);
    });
}

 

Bloc 사용하기

먼저 CounterBloc 의 인스턴스를 생성하여 사용할 수 있습니다.

Future<void> main() async {
  final bloc = CounterBloc();
  print(bloc.state); // 0
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  print(bloc.state); // 1
  await bloc.close();
}

 

Stream 사용법

cubit과 마찬가리조 Bloc은 Stream의 특수한 유형으로 Bloc을 구독하여 state를 실시간으로 업데이트 할 수도 있습니다.

Future<void> main() async {
  final bloc = CounterBloc();
  final subscription = bloc.stream.listen(print); // 1
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  await subscription.cancel();
  await bloc.close();
}

 

Bloc 관찰하기

bloc은 blocbase 를 extends하기 때문에 onChanged를 사용하여 Bloc의 모든 state 변화를 관찰 할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  Counter() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      emit(state + 1);
    })
  }
  
  @override
  void onChange(Change<int> change){
    super.onChange(change);
    print(change);
  }
}

 

다음 main.dart로 업데이트 합니다.

void main(){
  CounterBloc()
  ..add(CounterIncrementPressed())
  ..close();
}

 

Bloc 과 Cubir의 주요 차별화 요소는 Bloc 이 event 기반이기 때문에 state 변화를 유발한 원인에 대한 정보도 알수 있습니다.

이는 onTransition을 override하여 수행할 수 있습니다.

 

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  Counter() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      emit(state + 1);
    })
  }
  
  @override
  void onChange(Change<int> change){
    super.onChange(change);
    print(change);
  }
  
  @override
  void onTransition(Transition<CounterEvent, int> transition){
    super.onTransition(transition);
    print(transition);
  }
}

 

BlocObserver

이전과 마찬가리고 커스텀 BlocObserver에서 onTransition을 overrided하여 단일 위치에서 발생하는 모든 Transition을 관찰할 수 있습니다.

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change){
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
  
  @override
  void onTransition(Bloc bloc, Transition transition){
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
  
  @override
  void onError(Blocbase bloc, Object error, StackTrace stackTrace){
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

 

Bloc 인스턴스의 또 다른 독특한 특징은 Blocd에 새 event가 추가될 때마다 호출되는 onEvent를 override할 수 있다는 점입니다.

conChange 및 onTransition과 마찬가지로 onEvent는 전역뿐만 아니라 로컬에서도 override할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }

  @override
  void onEvent(CounterEvent event) {
    super.onEvent(event);
    print(event);
  }

  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition<CounterEvent, int> transition) {
    super.onTransition(transition);
    print(transition);
  }
}
class SimpleBlocObserver extends BlocObserver {
  @override
  void onEvent(Bloc bloc, Object? event) {
    super.onEvent(bloc, event);
    print('${bloc.runtimeType} $event');
  }

  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
}

 Bloc의 에러 처리

cubit과 마찬가지로 각 Bloc에는 addError와 onError메서드가 있습니다. Bloc 내부 어디에서나 addError를 호출하여 에러가 발생했음을 알릴 수 있습니다. 그런 다음 Cubit과 마찬가지로 onError를 override하여 모든 에러에 대응할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) {
      addError(Exception('increment error!'), StackTrace.current);
      emit(state + 1);
    });
  }

  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition<CounterEvent, int> transition) {
    print(transition);
    super.onTransition(transition);
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  }
}

 

이전 정리 : 2024.06.04 - [FLUTTER] - [Flutter] Bloc 개념 이해하기 1

 

[Flutter] Bloc 개념 이해하기 1

블록을 배우기 이전에 stream 의 개념을 알아야 더 이해하기 쉽다. Streams스트림은 비동기 데이터의 시퀀스이다.파이프는 Stream 이고 그 안을 흐르는 물을 비동기 데이터라고 이해하면 쉽다.dart에

kimsw509.tistory.com

Using a Cubit

이제 여러가지를 응용해 볼 차례이다.

 

Basic Usage

main.dart

void main(){
	final cubit = CounterCubit();
	print(cubit.state); // 0
	cubit.increment();
	print(cubit.state); // 1
	cubit.close();
}

 

위는 기본적으로 사용한 것이다.

 

Stream Usage

Future<void> main() async {
  final cubit = counterCubit();
    final subscription = cubit.stream.listen(print); // 1
    cubit.increment();
    await Future.delayed(Duration.zero);
    await subscription.cancel();
    await cubit.close();
}

위를 보면 CounterCubit을 구독 하고 있다가 상태 변화가 일어나면 print를 호출한다. 그 후 increment를 호출해서 상태 변화를 야기하고 cancel 해서 더이상 스트림을 구독하지 않는다.

 

Observing a Cubit

Cubit의 상태가 변하면 change가 호출된다. 이를 onChange를 통해 관찰할 수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
    
  void increment() => emit(state +1)
    
  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
    }
}

 

BlocObserver

블록옵져버를 이용하면 모든 상태 변화를 한곳에서 볼수 있다.

simple_bloc_observer.dart

class SimpleBlocObserver extends BlocObserver {
    @override
    void onChange(BlocBase bloc, Change change) {
    	super.onchange(bloc, change);
        print('${bloc.runtimeType} $change');
    }
}

 

Cubit Error Handing

모든 Cubit은 addError가 있어서 에러가 발생했을때를 알수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  
  void increment(){
    addError(Exception('increment error!'), StackTrace.current);
    emit(state +1);
  }
  
  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }
  
  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  } 
}

 

onError는 BlocObserver에서 override하여 전역적인 에러를 잡아낼수 있다.

simple_bloc_observer.dart

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('$bloc.runtimeType} $change');
  }
  
  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

 

블록을 배우기 이전에 stream 의 개념을 알아야 더 이해하기 쉽다.

 

Streams

스트림은 비동기 데이터의 시퀀스이다.

파이프는 Stream 이고 그 안을 흐르는 물을 비동기 데이터라고 이해하면 쉽다.

dart에서는 이를 쉽게 만들수 있다.

Stream<int> countStream(int max) async* {
	for (int i = 0; i < max; i ++ ){
    		yield i;
        }
}

 

aysnc* 를 사용함으로서 yield 키워드를 사용할 수 있고 stream 데이터를 반환한다. 위의 예는 integer stream을 반환한다.

위의 예를 비슷하게 사용하여 다른 값들을 반환할 수 있다.

 

Future<int> sumStream(Stream<int> stream) async {
	int sum = 0;
    await for (int value in stream){
    	sum += value;
    }
    return sum;
}

 

위의 함수를 보면 async 로 함수를 표시해서 await 키워드를 사용 할수 있고 Future 를 반환 가능해진다.

이렇게 대략 적으로 스트림을 봤으면 이제 Bloc 의 코어 패키지인 Cubit을 배울수 있다.

 

Cubit

CubitBlocBase를 상속한  클래스이며 여러 타입의 상태(state)를 매니지 할수 있도록 상속 가능하다. 

Cubit은 함수를 공개 하므로서 상태 변화 트리거를 걸 수 있다.

 

Creating a Cubit

 

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit() : super(0);
}

Cubit을 만들때는 어떤 타입의 상태를 컨트롤 할 것인지 정의해 줘야한다. 위의 CounterCubit의 경우 int 타입을 통해 상태를 다루게 되지만 복잡한 경우에는 원시타입이 아닌 클래스로 상태를 컨트롤해줘야한다.

 

그 다음으로 우리가 해줘야 할 것은 Cubit의 initial state를 정해주는 것이다. super 값에 값을 넣어줌으로서 해결이 가능하다.

 

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit(int initalState) : super(initialState);
}

 

Cubit State Changes

모든 Cubit은 emit 을 통해 상태를 바꿀수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit() : super(0);
    
    void increment() => emit(state + 1);
}

 

 

 

갑자기 위와 같은 에러 코드가 나오면서 testFlight 가 올라가지 않았다..

 

아카이브(Archive)도 잘 되고 실행도 잘되고 ( Flutter, xcode 전부) 이상하게도 스토어에만 올라가지 않았다.

그래서 구글로 서치를 해보다가 찾아 낸것이

https://www.reddit.com/r/swift/comments/1bd7kxj/swift_ios_cannot_upload_to_testflight/

 위와 같은 글인데 결국은 15.3부터 새로운 밸리데이션이 들어갔다는 뜻이다. 

내가 이해한 바로는 결국 사용하는 sdk들을 전부 최신 버전으로 업데이트를 하던가 아니면 내 앱의 버전을 낮추라고 되어 있었다.

그리하여 내 버전도 올리고 sdk들의 버전도 다 올려봤지만 ㅠㅠ 해결이 되지 않았다.

 

결국 해결한 것은. xcode를 삭제하고 (사용하던 버전은 15.3) 15.2버전의 xcode를 다운로드 했다.

(https://developer.apple.com/kr/xcode/resources/)

 

그리했더니 크게 무언가를 바꾸지도 않았지만 해결되었다.

+ Recent posts