JSON이란 JavaScript Object Notation의 약자로 JS에서 쓰는 객체 표시 방식이다 

 

JSON은 key- value쌍으로 이루어져 있는데

key는 무조건 String 타입이고, value에는 여러가지 타입이 들어갈수 있다 

(String, Numver, Booleanb, Array, Object, null등)

 

Dart에서  JSON데이터 사용방법

데이터를 주고받을떄는 JSON형식의 String으로 주고받음

  직렬화 -> Dart객체 ->  MAP -> String

  역직렬화 -> String -> MAP -> Dart객체 

  (항상 Map을 통해 변환한다)

 

클래스 만들때 fromJson 네임드 생성자, toJson 메서드를 만들어서 직렬화, 역직렬화를 한다

 

서버와 통신을 통해서 직렬화, 역직렬화가 꼭 필요하다(데이터를 주고받기 위해)

728x90

비동기 프로그래밍이란?

 

일단 동기(Syncronous)는 무엇이고 비동기(Asyncronous)는 무엇인가?

  -> 동기는 blocking되는 코드 -> 이전 명령이 안끝나면 다음명령이 실행 되지 않음

  -> 비동기는 blocking되지 않는 코드 -> 이전 명령이 안끝나도 다음명이 실행 됨 

 

왜 동기적인 작업이 문제가 될까? 예를들어

  -> 파일을 불러올때

  -> 데이터베이스에 데이터를 쓸때

  -> 네트워크로부터 데이터를 불러올때

등등 여러 케이스에서 한 작업이 끝날때까지 뒤의 모든 작업이 기다리게 된다. 

이럴 경우 시간적으로 손해가 크기때문에 동기화된 작업은 추천 되지 않는다 

 

그렇다면 비동기는? 작업이 완료될때까지 기다리지 않고 다음 작업을 바로 실행함(병렬적)

그렇다면 결과값은? 작업이 마무리되면 그때 데이터를 받음 

 

dart에서는 dart.async 라이브러리를통해 Future, Stream기능, 비동기 프로그래밍을 지원하게 된다 

 

Future을 알아보자

Future을 사용할 때 반환 되는 타입은 Future타입이다. Future타입안에 데이터타입이 명시 된다. 

Future<int> number = Future.value(1);
Future<String> name = Future.value('스트링');
Future<bool> isTrue = Future.value(true);

 

언제 쓰일까?

  -> 하나의 작업에 대해 이벤트가 한번 발생하는 단일 비동기 작업에 사용된다. 

void main(){
  int seconds = 2;
  print('start');
  Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  done
  waited 2
*/

 

비동기 작업을 동기화 처럼 사용하고자 할때(순서를 지켜서 실행해야할때) async, await키워드를 사용해서 코드를작성한다 

void main() async{
  int seconds = 2;
  print('start');
  await Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  waited 2
  done
*/

 

하지만 Future은 단 한번만 결과값을 받게 된다. 그래서 Stream을 사용한다

 

Stream에 대해 알아보자

제일 큰 특성은 시간에 따라 연속적인 데이터 흐름을 제공한다

연속적으로 데이터를 받을 수 있기 때문에 프로그램이 임의로 Stream을 종료하지 않는다

사용자가 Stream을 시작하고 끝을 내야한다.

 

예제로 알아보기전에 yield와 listen() method를 알아야 한다 

 

yield는 값을 방출하도록 하는 키워드 -> return 과 비슷하다고 생각하면 된다

listen()은 yield로 방출되는 값을 얻기위해 사용되는 메서드다

 

예제로 알아보자 

참고로 Stream을 적용한 함수에는 async* 키워드를 달아주어야한다 async가 아니다 

아래 예제는 입력받은 숫자부터 0까지 1초를 기다리면서 출력한는 예제이다 

Stream<int> emitNUmbers(int first) async* {
  for(var i = first; i >= 0; i--){
    yield i;
    
    await Future.delayed(Duration(seconds:1));
  }
}

void main() {
  emitNumbers(10).listen((number) {
    print(number);
  }
}

/*
  10
  9
  8
  7
  6
  5
  4
  3
  2
  1
  0
  이 차례대로 출려 됨 
*/

 

참고로 고차함수로 체이닝하여 사용할 수도 있다고 한다. 

체이닝을 사용하면 특정 값만 걸러내서 받을 수도 있을거같다. 

728x90

예외(Exception)에 대해서 알아보자

 

정의는 프로그램 실행동안 발생할 수 있는 예외적인 상황에서 발생하는 Exception 객체

 

종류에는 2가지로 나누어질 수 있다

  -> Dart가 사전정의한 예외. 자주 발생하는 예외로는 아래와 같이 있다

    -> DeferredLoadException - 라이브러리 사용시점에 라이브러리가 로드 되지 않았을떄

    -> FormatException - 타입이 서로 안맞을때(int를 String에 넣는다거나..)

    -> IOException - 입출력 관련 동작중 발생하는 예외(FIleDecriptor, socket, protocol등등..)

    -> OSError - 운영체제 레벨에서 발생하는 예외, 운영체제 관련해서 에러코드를 가지고 있다 

    -> TimeoutException - 비동기 결과 돌릴때 시간이 넘 오래걸리거나 할때 발생하는 예외 

  -> 사용자가 정의한 예외

    -> 위 예외에 없을때 사용자가 정의한 모든 예외 

    -> Exception클래스를 상속받아서 아래와 같이 예외 클래스를 만들면 됨. 

// 예외 클래스 생성
class AgeException implements Exception {
  final String message;
  
  AgeException(this.message);
  
  void printAge(int age){
    if (age < 0){
      throw AgeException(null);
    }
  }
  @override
  String toString() => messsage ?? 'AgeException 발생';
}

// 실제 예외 발생 코드
void main(){
  try {
    printAge(-8);
  } catch (e){
    print(e);
  }
}

 

구문을 좀더 자세히 살펴보면

  -> try - 시도하고자 하는 코드 

  -> catch - 발생한 예외를 잡는 코드 

  -> on - catch와 같이 쓰이며 try에서 발생하는 특정타입의 예외를 다루기 위해 사용

on IOException catch(e){
  ...
}

  -> finally - 예외 발생과 상관없이 무조건 마지막에 실행되는 구문

finally{
  ...
}

4개로 나누어진다 

 

마지막으로 throw가 있는데 특정 exception을 발생시키는 구문이다

throw [Exception이름];

 

위와 같이 예외를 발생 시킬수 있다 

728x90

클래스는 개념이 좀 많다

알아보자

 

클래스란? 

  -> 객채의 구조와 동작을 정의하는 틀

객체는?

  -> 클래스에서 정의한 구조를 바탕으로 생성된 실제 엔티티 혹은 데이터

 

클래스를 생성하려면 다음과 같이 정의하면 된다

아래 코드는 Person 클래스를 정의하고 있다 

속성으로 name, int를 가지며 생성자를 통해 초기화 하고, method로 introduce를 정의하였다 

// class [클래스이름] { ... }

class Person {
  String name;
  int age;
  
  Person(this.name, this.age);
  
  void introduce() {
    print("안녕, 나이는 $age, 이름은 $name");
  }
}

 

아래코드는 실제로 Person클래스를 사용해서 Person객체를 생성하는 코드이다 

void main(){
  Person p1 = Person('john',24);
  p1.introduce(); // 안녕, 나이는 24, 이름은 john 출력
}

 

클래스는 다음과 같은 구성요소들로 이루어져 있다. 이 같은 구성요소를 클래스의 맴버라고 한다

  -> 속성

    -> 인스턴스 변수(instance variable)

      => 객체에 속해있는 global scope 변수

    -> 지역변수(local variable)

      => method 코드블럭 안에 scoped된 변수

    -> 정적변수(static variable)

      => 클래스 변수라고도 한다 - 객체에 종속되지 않고 클래스 자체에 속하는 변수

      => 같은 클래스를 가지는 객체는 이 변수를 공유한다. 

      => static키워드로 선언한다 

      => Class이름으로 접근한다( Person.[static변수이름] 이런식으로)

      => 따라서 객체를 통해 접근할 수 없다

      => 값을 변경할수 있는데 *매우매우* 조심해서 변경하자. 

 

-> method

    -> 객체의 동작응 정의한다(속성을 변경하거나, 정보를 프린트하거나 등)

    -> 함수와 method차이? 함수는 클래스에 의존하지 않는다 

 

    -> 종류에는 2가지 인스턴스 메서드, 정적 메서드로 나누어진다

    -> 인스턴스 메서드(Instance Method)

      => 객체에 속해있는 method

      => this를 통해 접근 가능함

      => 클래스의 모든 곳에서 접근할 수 있다(scoped)

    -> 정적 메서드(static Method 혹은 class method)

      => method 앞에 'static' 키워드를 붙여야 한다

      => 역시 변수와 마찬가지로 클래스 이름으로 접근한다.

      => *중요* 내부에서 인스턴스 변수 사용 금지 

 

  -> 생성자(constructor)

    -> 기본생성자(default constructor)

      => 자동으로 생성되는 생성자이다 

      => 인스턴스 변수가 모두 초기화 되어있는 상태여야 한다.     

    -> 매개변수 생성자(parameterized constructor)

      => [클래스](this.인스턴스변수);

      => [클래스]([타입] [매개변수]) : this.인스턴스변수;

      => [클래스]([타입] [매개변수]) { this..인스턴스변수; }

      => 위 3가지로 매개변수 생성자를 쓸 수 있다. 

    -> 네임드 생성자(Named constructor)

       => 클래스 method와 같은 형식으로 호출하는 생성자. 예제는 아래와 같다(처음보는 형식이라 적어둠)

Class Car {
  String name;
  List<String> models;
  
  Car.fromList(List values)
    : this.name = values[0],
      this.models = values[1];
}

 

    -> this는 현재 객체를 가르키는 키워드이다. 

      -> this 를 사용해서 맴버 변수와 method에 접근하고 사용할수 있다

728x90

+ Recent posts