함수형 프로그래밍에 대해서 배워보자

 

첫번째 정의는 함수의 연속으로 프로그램을 구성하는 것이라고 생각하면 된다고 한다 

Method Chaining이라고도 한다고 한다 

  - ' . ' 을사용해서 여러개의 함수를 하나로 연결하는 방식

 아래 코드와 같이 여러 method 를 연속적으로 사용하는 방식

var result = number.abs().toString();

 

두번째 정의는 가변적인 데이터의 사용을 최소화하여 프로그램을 구성하는 방식이라고 하는데 

예제로 살펴보자 

아래와 같이 매개변수에만 의존하는 함수의 형태를 Pure FUnction(순수 함수)라고 한다 

int add(int a, int b){
  return a + b;
}

또다른 예제로는 아래와 같은 함수도 있다

잘 보면 매개변수를 의존하여 값을 계산하기 때문이다(스코프 안에서만 변경한다)

int getTotal(List<int> numbers) {
  int result = 0;
  for (var number in numbers){
   result += numbers;
  }
  return result;
}

 

함수형 프로그래밍에서 많이 사용하는 함수에는 2가지가 있다

  - 형변환 함수(Type Casting Function)

    -> 특정 데이터를 다른 타입의 데이터로 변경하는 함수(toString(), int.parse('')같은 함수)

  - 고차 함수(Higher-order function)

    -> map(), where(), reduce(), fold(), any(), every(), takeWhile()등  여러가지 종류가 있으며 중요한 몇가지 함수를 알아보자

   

      -> map()은  Collection타입의 데이터 각요소에 특정 함수를 적용한 새로운 collection을 반환하는 함수이다(shallow copy)

      -> 예제는 아래 코드와 같다. List의 모든 요소에 특정 작업을 해서 새로운 List를 반환하고 있다. 

//map(([매개변수]) { return [동작] });
List<String> fruitList = ['a', 'b', 'c'];
var delicious =. ruitList.map((fruit) {
  var word = '맛있는';
  word += fruit;
  reutrn word;
});
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

//map(([매개변수]) => [동작]); 
List<String> fruitList = ['a', 'b', 'c'];
var delicious = fruitList.map((fruit) => '맛있는 + $fruit');
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

     

      -> where()은 Collection 요소중 참인 조건에 부합되는 요소만 필터링한 컬렉션을 반환한다 

      -> 예제는 아래와 같다. Return안에 bool값을 판별할 수 있는 식이 존재해야 한다

      -> 자매품으로 firstWhere(), lastWhere()등이 있다만 얘네들은 참인 요소가 없을때는 에러를 뱉는다(stateError)

// where(([매개변수]) { return [조건식]; }
List<int> numbers = [1,2,3,4,5,6,7];
var result = numbers.where((number) {
  return number > 5;
});
print(result); // 6,7 출력

 

      -> reduce()는 collection에 있는 요소들을 하나의 값으로 결합하는 함수

      -> 예제는 아래와 같다, 리스트를 순회하면서 앞에 값을 뒤에 값에 계속더하면서 진행된다

      -> 특징은 collection의 데이터타입 과 같은 타입으로만 반환할 수 있고 데이터가 비어있을경우 에러가 발생한다(stateError)

// reduce(([매개변수1], [매개변수2]) { return [동작] };
List<int> numbers = [1,2,3,4,5];
var result = numbers.reduce((a, b) {
  return a + b
});
print(result); // 15 출력

 

      -> fold()는 reduce에서 초기값이 추가된다. reduce()와는 다르게 초기값이 초기값으로 세팅된다. 

      -> 특이사항으로는 다른 타입으로도 반환이 가능하고 요소가 없어도 에러가 발생 안한다고 한다. 

// fold(초기값, ([매개변수1], [매개변수2]) { return [동작] });
// 앞에 초기값이 추가된다

List<int> numbers = [1,2,3,4,5];
var result = numbers.fold(0, (a, b) {
  return a + b
});
print(result); // 15 출력

 

 

      -> any()는 주어진 collection중 하나라도 조건을 만족시 true를 반환한다(where과 비슷하지만 bool을 반환한다)

      -> every()는 collection에서 모든 요소가 맞아야 true가 반환된다 

      -> takeWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 반환하고 false로 나오는 요소부터는 무시하는 기능이다

      -> skipWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 스킵하고 false로 나오는 순간부터의 요소를 모두 반환한다

 

Method Chaining은 위 고차함수를 잘 조합해서 쓰는게 핵심이다 

map() + where(),

where() + reduce(),

where() + map() + fold() 등등

Chaining을 통해 collection에서 필요한 여러 작업을 짧은 코드수로 끝낼 수 있다

Function(함수) 

  - 입력을 받아 특정작업을 수행하고 반환값을 return하는 코드 블록

  - 구성요소로는 (반환타입, 함수이름, 매개변수, 실행할 코드, 반환 값)이 있음

  - 아래 코드에서 각각

      반환타입은 int,

      함수 이름은 hi,

      매개변수는 int a, int b,

      실행할 코드는 return a + b,

      반환값은 a + b 이다

int hi(int a, int b){
  return a + b;
}

 

  - 아래와 같이 위 코드를 한줄로 표현 가능하다(한줄로 표현할 수 있을때만)

int hi(int a, int b) => a + b;

 

Generic(제네릭)

  - 클래스, 함수에서 데이터타입을 일반화 하여 다양한 타입을 지원할수 있게 하능 기능

[타입파라미터] [함수이름]<타입파라미터>([매개변수]) { ... }

T getFirstElement<T>(List<T> list) { 
  return list[0];
}

 
 - 특정타입에 의존하지 않고 여러 타입의 데이터에 대해 동일한 코드를 적용할 수 있어서 재사용성 높은 코드를 짤수 있다. 

 

 

Collection(컬렉션)

  - 여러 값을 그룹으로 묶어서 효율적으로 관리하는 데이터 셋

  - 종류는 List Set Map이 있다

    - List -> 순서가 있는 데이터 셋, 선언할 때 꺽쇠(<>) 사이에 데이터 형을 넣어줘야한다(int의 LIst는 -> List<int>)

      - [ ] 기호를 사용한다

      - List안의 요소가 변동 될 경우 var로 선언해 줄 수 있고, 없는 경우 final, const로 선언 가능하다, 단 요소를 변경할 수 없다 

      - 타입추론으로 LIst선언가능 (var sample = [1,2,3] 이런식으로도 선언 가능하다는 말이다 )

    - Set -> 중복되지 않은 값들이 묶인 데이터 셋!(중복을 허용하지 않는다)

      - { } 기호를 사용한다

      - 참고로 빈 Set을 만들고 싶을때는 var names = <String>{}; 와 같은 형식으로 만든다

        - var numbers = {}. 로 선언하면 map을 만드는 것임 

      - 순서가 없다, Index가 없다는말임

    - Map -> Key, Value가 하나로 1:1매칭되는 값을 가지는 데이터 셋

      - 선언할때는 Map<String, String> sample = { 'k1': 'v1', 'k2':'v2 } 이런식으로 선언한다

      - 특이사항으로는 키는 중복 불가하지만 값은 중복으로 넣을 수 있다

      - [변수이름][[키 이름]] 을 통해 값을 검색하거나 수정할 수 있다

      - keys, values를 통해 각 키 값을 모두 반환할 수 있다

 

    - List와 Set의 차이? 순서, Index, 중복여부 

 

Enumerations(열거형)

  - 여러 '상수' 값을 묶은 데이터 셋

  - 예제 -> enum Color { red, green, blue } 

  - 특징으로는 

      - 열거형 이름을 통해 열거형 값에 접근 가능 ( var Color = Color.blue }

      - switch문을 통해 각 case로 분기할 수 있음

      - index를 통해 열거형에 포함된 값이 열거형의 몇번째 있는지 알수 있음 (Color.red.index -> 0 )

  - values를 통해 포함되어있는 값을 알 수 있음 ( var colors = Color.values 로 할경우 List<Color> 로 추론됨)

  - name을통해 포함되어있는 값을 알 수 있음 ( Color.red.name -> red 값을 가짐)

  - 언제 사용함? 연관된 상수들의 집합을 정의할때 사용한다고 -> 같은 개념의 여러 값을 지정할때 사용함

 

 

컴파일러란? 

  -> 사람이 쓰는 고급 프로그래밍언어를 컴퓨터의 저급 프로그래밍 언어(Assembly, 기계어)로 변환 시키는 프로그램

 

컴파일러 종류에는 크게 2가지로 나누어진다

  -> JIT(Just-In-Time)컴파일러 - 런타임에 코드 일부 또는 전부를 컴파일링 하는 기술

       장점 -> 실행결과를 실시간으로 확인 할 수 있다

       단점 -> 상대적으로 느리다, 실행중에 계속 컴파일링 하기 때문에 

 

  -> AOT(Ahead-Of-Time)컴파일러 - 런타임 전에 코드를 컴파일링 하는 기술 

       장점 -> 컴파일이 완료 되어있기 떄문에 실행속도가 빠름

       단점 -> 코드를 고치자 마자 눈으로 확인이 힘들다 

 

Dart언어는 신기하게도 2가지 컴파일러를 모두 사용한다고 한다!

AOT로 성능을 높이고 JIT로 실시간 결과를 확인할 수 있다고 한다

 

또한 Dart는 멀티 플랫폼 개발을 지원한다

(멀티 플랫폼이란 WEB, Mobile, DeskTop환경등을 말한다)

 

Why Not Learning? 

 

개발환경 구축

개발환경을 구축하기 위해 SDK를 설치해야 한다. 

(SDK란? Software Development Kit의 약자로 개발 패키지라고 생각하면 된다)

 

SDK는 총 5가지(컴파일러, 라이브러리, 디버깅툴, 문서, 패키지관리자 등)로 이루어져 있다 

 

SDK를 받을떄 주의할점은 Stable버전을 사용해야 한다!

Beta나 Alpha를 받아 쓰면 안정성이 떨어진다

 

macOS기준 설치하는 방법

  1. Homebrew가 설치되어 있지 않으면 설치한다

  2. brew명령어로 dart를 설치한다 

xcode-select --install
brew tap dart-lang/dart
brew install dart

 

  3. 설치 완료 후 프로젝트 를 생성한다 

dart create [프로젝트이름]

 

  4. 생성된 폴더를 VScode로 열어주면 된다 

 

플러터 팀 소개앱 제작 프로젝트를 진행하면서 

크게 강의에서 배웠던 WIdget의 종류에 대하여 학습한 부분을 실제로 적용하는 부분과

상태관리를 적용해 보는 시간을 보냈다 

 

실제 적용을 하는 것과 코드를 복붙하는것에 확실히 차이가 있었다. 

 

아래는 이번주 작성한 TIL이다. 

https://teem0.tistory.com/20

 

[내일배움] 플러터 뷰위젯(viewWidget) 종류

플러터에는 View Widget으로 크게 4종류가 있다 PageView, ListView, GridView, TapbarView,  하나하나씩 살펴보자  PageView는 아래 사진과 같이 슬라이드 형식으로 애니매이션이 들어가 있는 뷰이다  코드

teem0.com

https://teem0.tistory.com/21

 

[내일배움] 레이아웃위젯(LayoutWidget) 종류

플러터에서 레이아웃을 설정하는 위젯에 대해 알아보자 크게 Container, SizedBox, Row, Column, Expanded, Stack&Positioned로 총 6개정도로 나누어진다고 한다 역시 하나하나씩 알아보도록 하자(예제사진을

teem0.com

https://teem0.tistory.com/22

 

[내일배움] 플러터 GetX 상태관리 라이브러리

상태관리로써 StatefulWidget을 사용할 수도 있지만 한계가 존재한다  상태관리가 복잡해 진다거나, 단순화 하려고 하면 성능에 이슈가 생기거나 하는 식이다 여러 이슈들을 해결하기 위해 GetX라

teem0.com

https://teem0.tistory.com/23

 

[내일배움] 플러터 url 외부 연결

플러터에는 기본적으로 url을 연결할 수 있는 하이퍼링크 기능이 없다... 한참 삽질끝에 방법을 알아내었다  일단 다음과 같이 url_launcher플러그인을 설치한다. flutter pub add url_launcher 그 다음 ht

teem0.com

 

팀원과 업무를 나누어서 하는 부분은 매우 즐거웠다. 

각자 꾀부리지 않고 열심히 묵묵히 하는 모습을 보며 고마움을 느꼇다. 

그렇기에 나도 더 열심히 해야 하는 동기부여갸 되어 더 열심히 한것 같다. 

 

'창업 > WIL(Weekly I Learned)' 카테고리의 다른 글

[내일배움] WIL-3  (1) 2024.11.08
[내일배움] WIL-2  (2) 2024.11.01

플러터에는 기본적으로 url을 연결할 수 있는 하이퍼링크 기능이 없다...

 

한참 삽질끝에 방법을 알아내었다 

 

일단 다음과 같이 url_launcher플러그인을 설치한다. 

flutter pub add url_launcher

 

그 다음 https로 연결을 위해 ios에서 권한을 부여해 줘야 한다

권한은 root -> ios -> Runner -> Info.plist에서 

<dict> </dict> 사이에 다음과 같이 추가한다

sms, 전화는 각각 string 안에 sms, tel값을 추가하면 된다 .

<key>LSApplicationQueriesSchemes</key>
    <array>
      <string>https</string>
    </array>

 

 

다음에 버튼이나 이벤트리스너에서 다음과 같이 Uri를 생성하고 url을 launch 해주면 된다 

잊지말고 async를 붙여주도록하자 

final url=Uri.parse('https://google.com');
await launchUrl(url);

 

상태관리로써 StatefulWidget을 사용할 수도 있지만 한계가 존재한다 

 

상태관리가 복잡해 진다거나, 단순화 하려고 하면 성능에 이슈가 생기거나 하는 식이다

 

여러 이슈들을 해결하기 위해 GetX라는 3rd party Library가 만들어 졌다.

기능도 아주 많고 빠르다고 하니 앞으로는 상태관리로 이 친구를 많이 쓸거같다 

 

기본적으로 설치는 프로젝트에서 다음과 같이 진행된다 

flutter pub add get

 

 

이후 상태관리를 하려고 하는 클래스에서 다음과 같이 extends로 상속한다

import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:plmanchu/info/memberInfo.dart';

class Membercontroller extends GetxController {
  List<Memberinfo> members = [
    Memberinfo(
        name: "***",
        MBTI: "****",
        image: 'lib/assets/images/2.png',
        hobbies: ["피아노", "드럼"],
        major: "컴공",
        careers: ["클라우드엔지니어", "k8s개발자"],
        targetApp: "당근마켓",
        introduce: "잘부탁드립니다",
        radarChart: 'lib/assets/images/radarChart.png'),
  ];
}

주의해야 할 점은 member class가 따로 존재해야 한다는 것이다. 

 

이후 main.dart파일에서 의존성을 주입하기 위해 다음과 같이 GetX를 적용한다

* 주의할점 GetX를 사용하면 MaterialApp에서 GetMaterialApp으로 타입을 변경해야 한다

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:plmanchu/info/memberInfo.dart';
import 'package:plmanchu/pages/intro.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    Get.put(Membercontroller())
//  return MaterialApp(
    return GetMaterialApp(

      title: 'PlmanchuIntroducing',
      // theme 은 설정 안해주나요?
      home: IntroPage(),
    );
  }
}

 

 

이후 GetX의 컨트롤러를 사용해서 이벤트를 작동시키기 위해 아래와 같이 

Get.find<controller이름>().(method명)(); 으로 작동시킨다  

아래 코드에서 builder안에, 그리고 Image안에 각각 맴버별로 이미지와 정보를 생성함을 알수 있다. 

children: [
      ...List<Widget>.generate(
          Get.find<Membercontroller>().getMemberLength(), (index) {
        return GestureDetector(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => specA(
                        memberInfo: Get.find<Membercontroller>()
                            .getMember(index),
                      )),
            );
          },
          child: Image.asset(
              Get.find<Membercontroller>().getMember(index).image),
        );
      }),
    ],

 

내부에서 동적으로 데이터가 변경되는 경우 다음과 같이 update() 함수를 사용하여 setState() 와 같이 상태를 업데이트 시켜 준다 

update();

 

이렇게 update를 트리거 시켜주면 GetBuilder로 구독한 곳에서 업데이트가 일어나게 된다 

위 소스코드상에는 GetBuilder로 업데이트되는 부분이 현재 없기 때문에 Update시킬 필요가 없다 ㅎ

구독랑이를 하려면 아래와같이 GetBuilder와 controller를 연결시켜주면 쓸수 있다

* 주의할 점은 init과 builder는 반드시 들어가야 하고 builder의 argument는 임의의 컨트롤러 이름이다

child: GetBuilder{
	init: Get.find<컨트롤러 이름>,
	builder: (controller) => {
		Widget...
	}
}

플러터에서 레이아웃을 설정하는 위젯에 대해 알아보자

 

크게 Container, SizedBox, Row, Column, Expanded, Stack&Positioned로 

총 6개정도로 나누어진다고 한다

 

역시 하나하나씩 알아보도록 하자(예제사진을 첨부하지 않겠음)

 

제일먼저 Container

코드를 살펴보면

Container(
  padding: const EdgeInsets.only(
    left: 20,
    right: 20,
  ),
  decoration: BoxDecoration(
    gradient: LinearGradient(
      colors: [
        Color.fromARGB(255, 255, 59, 98).withOpacity(0.7),
        Color.fromARGB(255, 255, 59, 98)
      ],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    ),
    borderRadius: BorderRadius.circular(10),
    boxShadow: [
      BoxShadow(
        color: Color.fromARGB(255, 255, 59, 98).withOpacity(0.5),
        spreadRadius: 5,
        blurRadius: 7,
        offset: Offset(0, 3), // changes position of shadow
      ),
    ],
  ),
  width: 200,
  height: 150,
  child: Center(
      child: Text(
    'Container',
    style: TextStyle(color: Colors.white),
  )),
),

 

이코드 같은 경우 width 200 height 150의 컨테이너가 생성되고 

안에 그라디언트(decoration옵션)로 투명을 준 색깔이 컬러풀하게 빛나는 걸 확인 할 수 있다. 


다음으로 SizedBox

코드로 살펴보자

 

Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Container(
        color: Colors.red,
        width: 100,
        height: 40,
      ),
      const SizedBox(height: 10),
      Container(
        color: Colors.blue,
        width: 100,
        height: 40,
      ),
    ],
),

 

특별한게 없다 각 요소 혹은 widget사이에 padding처럼 역할을 수행할 수 있는 SizedBox이다 

height옵션은 높이 즉,위아래 간격으로 나눌때 width옵션은 너비, 즉 좌우로 나눌때 쓰면 된다. 


다음으로 Row 와 Column

코드로 살펴보자 

 

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: List.generate(
    5,
    (index) => Container(
      width: 40,
      height: 40,
      color: Colors.red,
      margin: const EdgeInsets.all(5),
    ),
  ),
),

 

Row로 선언 이후에 LIst.generate로 5개 요소를 균등하게 만들어 주고 있다

Row는 말그대로 가로방향으로 요소를 나열하는 LayoutWidget이다 

Column은 Row와 반대로 세로방향으로 요소를 나열하는 LayoutWidget이다 라고 알아두면 된다


다음으로 Expanded

역시 코드로 살펴보자 

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Expanded(
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    ...List.generate(
      4,
      (index) => Container(
        width: 40,
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
  ],
),

 

Row & Column을 사용할때 많이 합쳐서 사용한다고 한다 

Expanded뜻은 확장하다라는 뜻인데 그냥 Row로 선언한 요소의 경우 딱 그 필요한 width만 차지하는 반면

Expanded는 가로영역 전체를 다 차지하게 된다. 

 

CSS에서 존재하는 Flex옵션도 있는데 비율로 자리를 차지하는 옵션이다.

코드는 다음과 같다 

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Expanded(
      flex: 1, // 1
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    Expanded(
      flex: 3, // 2
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    Expanded(
      flex: 2, // 1
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
  ],
),

마지막으로 Stack&Positioned

사진으로 간단하게 살펴보면 아래 사진과 같이 프로필 밑에 사진버튼같은걸 더할때 Stack을 사용하게 된다. 

코드로 살펴보자 

Stack(
  children: [
    const CircleAvatar(
      radius: 50,
      child: Icon(
        Icons.person,
        size: 40,
      ),
    ),
    Positioned(
      bottom: 0,
      right: 0,
      child: Container(
        padding: const EdgeInsets.all(7),
        decoration: const BoxDecoration(
            shape: BoxShape.circle, color: Colors.white),
        child: const Icon(
          Icons.camera_enhance,
          size: 24,
        ),
      ),
    )
  ],
),

 

Stack을 선언하고 아래 children으로 CircleAvatar과 그 위에 띄워지는 Positioned로 선언된 카메라 아이콘이 있다 

Bottom, Right 옵션을 주면 우하단에 Stack이 위치하게 되며 

Top, Left 옵션을 주게되면 좌상단에 Stack이 위치한다

 

 

 

이렇게 LayoutWidget의 종류를 알아보았다. 

 

PageView와 함께 사용하면 여러가지 레이아웃을 만들수 있을거같다. 

 

 

+ Recent posts