[Flutter] 스나이퍼팩토리 14일차

KWANWOO·2023년 2월 13일
1
post-thumbnail

스나이퍼팩토리 플러터 14일차

14일차에서는 다른 사람의 코드를 가져다 쓸 수 있는 패키지들에 대해 학습했다.

학습한 내용

  • 3주차 주간 평가 퀴즈앱 만들기 학습
  • pub.dev
  • 패키지

추가 내용 정리

Linting

린트(Lint) 또는 린터(Linter)는 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시를 달아놓기 위한 도구들을 의미한다.

린트 설정을 통해서 규칙적인 코딩 스타일을 만들 뿐 아니라 런타임 에러도 줄일 수 있다. 또한 팀 단위로 개발을 진행할 때 코드 룰을 설정하여 스타일에 일관성을 줄 수 있다.

Linter rule 설정 방법
analysis_options.yaml 파일에서 린터 룰을 설정

  • 설정 예시
 linter:
    rules:
      missing_reauired_param: true
      prefer_const_declarations: true
      prefer_const_constructors: true

더 자세한 린터 설정 방법은 아래 링크를 참고
[Flutter : 플러터 ] Linting 설정으로, 흔하게 실수할 수 있는 것을 build 전 방지하기.

pubspec.yaml

puhspec.yaml에 대해서는 [Flutter] 스나이퍼팩토리 3일차 에서 정리한 적이 있지만 다시 한 번 정리해보고자 한다.

pubspec.yaml은 플러터 프로젝트에서 사용하는 모든 메타 데이터(패키지, 프로젝트 버전, 자원 등)의 종속성을 명시하고, 관리하는 파일이다.

YAML은 직렬화 언어로 Key:Value의 구조로 명시하며, 들여쓰기 및 Array(-) 표기법 등의 작성법이 존재한다.

  • name : 패키지의 이름, 모든 패키지에 필요
  • description : 패키지에 대한 설명을 적는 곳, 개인 패키지일 경우 옵션이지만 패키지를 게시하려면 필수
  • publish_to : 배포할 곳을 지정, 기본 값은 https://pub.dev/ 이고 none은 패키지로서 배포하지 않겠다는 의미
  • version : 패키지의 버전을 의미, 기본값(1.0.0+1), 지정 안 할 경우(0.0.0)
  • environment : Dart SDK 환경을 설정하는 속성
  • dependencies : 패키지의 의존성을 작성, 주로 외부 패키지를 가져다 쓰기 위해 사용
  • dev_dependencies : 패키지가 사용하는 개발용 의존성을 작성
  • author / authors : 해당 패키지를 만든 사람을 표기
  • homepage : 해당 패키지의 홈페이지가 있는 경우 표기
  • documentation : 해당 패키지의 문서가 있는 경우 표기
  • dependency_overrides : 임시로 의존성을 오버라이드 해야하는 경우 작성
  • executables : 실행가능한 패키지인 경우 스크립트를 작성

Flutter Package(패키지) 사용

자세한 플러터 패키지 사용 방법은 아래의 공식 문서를 참고
패키지 사용하기 - Flutter

플러터 패키지는 많은 기업과 개발자들이 공유한 코드이다. 이 패키지들을 활용하여 여러가지 기능을 직접 구현하지 않고 활용할 수 있다.

  • Command
flutter pub add pakage_name

커맨드 창에 위 명령어를 입력하면 패키지가 다운로드 및 적용된다.

또는 아래 예시와 같이 pubspec.yamldependencies에 추가하고 flutter pub get을 실행해도 된다. (VScode와 같은 IDE 사용 시 저장하면 자동으로 설치된다.)

dependencies:
  flutter:
    sdk: flutter

  bloc: ^8.0.2  # flutter pub add bloc
  flutter_bloc: ^8.0.1 # flutter pub add flutter_bloc
  • 패키지 찾기
    다양한 플러터의 패키지는 pub.dev에 접속하여 찾을 수 있다.

원하는 패키지에 들어가면 설치, 사용 방법, 버전 정보 등을 확인할 수 있다.

많이 사용되는 패키지들

주요 pub 명령어

flutter에서 주로 사용되는 pub 명령어는 아래와 같다.

flutter pub version # 버전 확인

flutter pub deps # 의존성 출력 (트리 형태로 출력한다.)

flutter pub add [패키지 이름] # 의존성 추가 (자동으로 yaml 파일이 수정된다.)

flutter pub remove [패키지 이름] # 의존성 제거 (자동으로 yaml 파일이 수정된다.)

flutter pub outdated # 업그레이드가 필요한 플러그인을 찾아준다.

flutter pub upgrade # 업그레이드가 가능한 플러그인을 업그레이드 한다.

flutter pub get # 플러그인들을 사용할 수 있게 프로젝트로 가져온다.

더 많은 pub 명령어는 아래의 링크를 참고
flutter pub 명령어

버전 표기법

버전(version)은 일반적으로 배포되는 패키지 또는 프로그램에 붙여지며 수정이나 보완이 이루어졌을 경우 바뀐다.

보통 버전은 세 자리의 숫자와 마침표로 이루어지는데 각각의 숫자는 Major Minor Patch를 의미한다.

  • Major: 엄청나게 큰 변화가 일어났을 때 변경
  • Minor: 기능적인 면에서 변경점이 있거나 추가된 사항이 있을 때 변경
  • Patch: 사소한 변경이 이루어졌을 때 증가, 대표적으로 오류를 잡아내는 경우에 변경

이러한 버전의 예시는 아래와 같다.

버전 표기 예시

  • 처음 Release: v1.0.0
  • 처음 Release 후 작은 오류 수정: v1.0.1
  • 기능 상의 추가 및 변경: v1.1.0
  • 작은 오류를 11번에 걸쳐 수정: v1.1.11
  • 이전과 호환이 안될 정도로 큰 변화: v2.0.0

tilde와 caret syntax

  • tilde(~)
    x.y.z 중 z범위 내에서만 버전 업데이트

  • caret syntax(^)
    x.y.z 중 x이하 하위 호환성이 보장되는 범위 내에서 버전 업데이트


14일차 과제

  1. HTTP요청을 보낼 때 사용되는 Query Parameter와 Header
  2. HTTP 인증 방식
  3. 아래로 당겨서 새로고침하는 UI 만들기
  4. “Slider, Carousel”를 주어진 패키지를 사용하여 만들기

1. HTTP요청을 보낼 때 사용되는 Query Parameter와 Header

REST API

REST는 Representational State Transfer의 약자로 HTTP URI를 통해 자원(resource)을 명시하고 HTTP 메소드(POST, GET, PUT, DELETE, PATCH 등)을 통해 해당 자원에 대한 CRUD operation을 적용하는 것을 의미한다.

REST API란 REST의 원리를 따르는 API를 의미하는데 이러한 REST API의 파라미터에는 아래와 같은 네 가지 타입이 있다.

  • header 파라미터
  • request body 파라미터
  • query string 파라미터
  • path 파라미터

REST API에 대한 자세한 내용은 아래 링크를 참고 (나중에 따로 학습하여 포스팅도 할 예정)
[네트워크] REST API란? REST, RESTful이란?

Header는 HTTP 요청 그 자체에 대한 정보를 담고 있는데 주로 인증과 권한 부여 두 가지를 목적으로 사용된다.

read only API의 경우 키가 필요 없는 경우도 있지만 대부분의 상업용 API는 API키나 다른 메소드를 통한 허가를 필요로 한다. 이러한 보안이 없다면 사용자들은 특별한 등록 없이 API를 무제한으로 요청할 수 있어 문제가 될 수 있다.

또한 인증 절차가 없다면 사용자 데이터를 가져올 때 어려움을 겪을 것이고, 악의적인 사용자로부터 데이터를 보호할 수 없다.

그리고 누가 나의 API를 사용하는지 혹은 어떤 엔드포인트가 가장 자주 사용되는지 알 수 없기 때문에 API 개발자는 반드시 인증과 허가에 대한 방안을 생각해야 한다.

권한 부여의 종류

  • API Key
  • Basic Auth
  • HMAC(Hash-based Message Authorization Code)
  • OAuth 2.0

위의 예시들 중에서 API Key와 Basic Auth 두 가지만 자세히 살펴보면 아래와 같다.

1. API Key

{"api-key": "012asfaawe2342ljsfsndsdfaaabbb"}

API Key는 리퀘스트 URL이나 리퀘스트 헤더에 포함되는 긴 문자열이다. 보통 API를 사용하는 사람을 식별하는 역할을 한다.

키의 종류에는 public과 private가 있는데 public은 보통 리퀘스트에 포함되고, private는 패스워드와 같아 서버와 서버간의 통신에서만 사용된다.

2. Basic Auth

Authorization: Basic bG9sOnNlY3VyZQ==

리퀘스트 헤더에 username:password를 넣어 인증하는 방식이다. username과 password는 Base64로 인코딩된다.

Basic Auth를 사용하는 API는 HTTPS를 사용하는데 이는 주고 받는 메세지가 HTTP 프로토콜 내에서 암호화된다는 뜻이다.

API 서버가 메세지를 받을 때 메세지를 복호화 하여 헤더를 읽어낸다. 문자열을 디코딩하고 username과 password를 분석한 뒤 리퀘스트를 받을지 말지를 결정한다.


이외의 Header 파라미터의 예시는 아래 링크를 참고
재미있는 REST API 파라미터의 종류와 개요

Request Body

보통 POST 리퀘스트에서는 JSON 오브젝트를 리퀘스트 바디안에 넣어 보낸다. 이것이 Request Body 파라미터이며, 주로 JSON으로 되어있다.

{
	"days": 2,
	"units": "imperial",
	"time": 1433524597
}

Query Parameter

Query String 이라고도 하며 데이터를 전송하기 위해 사용하는 GET, POST 방식에서 각각의 경로를 설정할 때 사용한다.

아래 예시처럼 ? 뒤에 id란 변수에 값을 담아 서버에 전달하는 방식이 Query Parameter이다. 즉, users에 담긴 정보 중에서 id 123번의 자료를 요청하는 것이다.

/users?id=123 // 아이디가 123인 사용자를 가져온다.

Path Variable

Path Variable은 Query Parameter와 마찬가지로 서버에 경로를 지정하여 데이터를 요청하는 방법 중 하나이다. 아래와 같은 방식으로 사용한다.

/users/123 // 아이디가 123인 사용자를 가져온다.

Query Parameter와 Path Variable을 사용하는 경우

일반적으로 어떤 데이터의 위치를 특정해서 보여줘야 할 경우 Path Variable을 사용하고, 정렬하거나 필터링을 할 경우 Query Parameter를 사용한다. 각 방법을 사용하는 경우에 대한 예시는 아래와 같다.

/users  // 사용자 목록을 가져온다.
/users?occupation=programer  // 프로그래머인 사용자 목록을 가져온다.
/users/123  // 아이디가 123인 사용자를 가져온다.

Query Parameter와 Header의 차이

위의 설명대로 Query Parameter와 Header의 차이는 사용 용도에 있다.

Header는 요청에 대한 전체적인 정보를 담고 있으며, 보통 인증과 권한 부여를 위해 사용하고, Query Parameter는 요청할 데이터의 경로를 설정할 때 사용한다.

여기서 데이터를 요청할 때 특정 데이터를 필요로 할 때와 정렬이나 필터링 할 때를 나눠 상황에 맞게 Path Variable 또는 Query Parameter을 사용한다.

2. HTTP 인증 방식

누구나 요청할 수 있는 HTTP 통신에 응답값을 인증된 사용자 혹은 로그인 된 사용자에게만 전달할 수 있도록 인증하는 방식에는 여러가지 방법들이 있는데 대표적으로 아래의 4가지 방법이 있다.

  • BASIC 인증
  • DIGEST 인증
  • SSL 클라이언트 인증
  • 폼 베이스 인증

아래의 방법들을 사용하여 HTTP 요청에 대한 인증을 수행하고, 인증된 사용자에게만 응답을 전달할 수 있다.

BASIC 인증

BASIC 인증은 클라이언트에 대한 사용자 이름과 암호를 Base64로 인코딩 된 문자열을 보내는 방식으로 앞에서 설명한 Header의 Basic Auth 내용과 같다.

간편하며 널리 사용되지만 패킷 도청에 취약하다는 단점이 있다. 따라서 리소스 보안을 강하게 해야 할 경우에는 다른 인증 방식을 사용하는 것이 좋다.

DISEST 인증

DISEST 인증은 사용자명, 패스워드 등을 조합하여 MD5 값으로 인증한다. 기본 인증보다 보안이 강화된 인증 방식이며 아래의 과정을 거친다.

  1. 클라이언트 - 페이지 요청
  2. 서버 - digest 인증 필요하다고 통보
  3. 클라이언트 - 사용자가 아이디/패스워드 입력하면, 서버정보+클라이언트 정보 병합 후 MD5로 암호화 하여 전송

DISET 인증 방식에서 서버는 클라이언트로 단순히 패스워드를 받는 것이 아닌 MD5로 해시된 값을 받는다.

MD5

  • Message Digest algorithm 5
  • 128비트 암호화 해시함수
  • 암호화, 체크섬 무결성 검사 등에 사용
  • 임의의 길이 문자열 입력 -> 128비트 문자열 출력

SSL 클라이언트 인증

사용자 아이디와 패스워드가 도난 당했을 경우 제 3자가 접근할 수 있다. 이를 방지하기 위한 대책 중 하나가 SSL 클라이언트 인증이다.

HTTPS의 클라이언트 인증서를 사용하는 인증 방식으로 단독으로 사용되지 않고, 아래에서 설명할 폼 베이스 인증과 합쳐져서 사용되고 있다.

단점으로는 클라이언트 증명서 구입 비용이나 서버의 운영자 자신이 인증 기관을 만들어서 안전하게 운영하기 우해 들어가는 비용 등이 많이 필요하다는 점이다.

폼 베이스 인증

인증의 대부분은 폼 베이스 인증이다. HTTP 가 표준으로 제공하는 BASIC, DIGEST 인증은 사용상의 문제, 보안적인 문제로 거의 사용되지 않는다. SSL 클라이언트 인증도 비용 등의 문제로 잘 사용되지 않는다.

폼 베이스 인증은 HTTP 프로토콜로서 사양이 정의되어 있는 인증 방식은 아니다. 클라이언트가 자격 정보를 송신하여 검증 결과에 따라 인증을 하는 방식이다.

따라서 웹 어플리케이션 마다 제공되는 인터페이스나 인증의 방법이 다양하다. 아래는 폼 베이스 인증을 진행하는 로그인 필드의 예시이다.

폼 베이스 인증의 경우 기능 구현에 대한 표준적인 문서가 존재하지 않기 때문에 어플리케이션에서 맞는 인증을 구현하여 사용해야 한다.

공통 사양이 없기 때문에 웹 사이트 별로 다르게 구현하고 있으며, 안전한 방법으로 구현 시 높은 보안 등급을 유지할 수 있다.

또한 방금 전 인증 성공 유저라는 상태를 프로토콜 레벨에서 유지할 수 없어 유저 구분을 하는 세션 관리를 위해 쿠키를 사용하고 있다.

3. 아래로 당겨서 새로고침하는 UI 만들기

화면을 아래로 당겨서 새로고침을 하는 것은 UX 관점에서 매우 편리하고 새로운 데이터를 요청하고 보여주기 위한 좋은 수단이 된다.

아래 주어진 데이터와 지정된 패키지를 사용하여 아래로 당겨서 새로고침하는 UI를 만들었다.

  • 사진 리스트
final imageUrl = [
  "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
  "https://images.pexels.com/photos/33053/dog-young-dog-small-dog-maltese.jpg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
  "https://images.pexels.com/photos/2664417/pexels-photo-2664417.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
  "https://images.pexels.com/photos/10361796/pexels-photo-10361796.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
  "https://images.pexels.com/photos/9409823/pexels-photo-9409823.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
];
  • 패키지 명
    pull_to_refresh

  • 결과물 예시
    • 아래로 당기면 새로운 랜덤 이미지로 바뀐다.
    • pull_to_refresh 패키지를 사용할 때 header: WaterDropHeader()를 활용한다.

코드 작성

  • pubspec.yaml
dependencies:
	pull_to_refresh: ^2.0.0

dependenciespull_to_refresh를 넣어 패키지를 설치했다.

  • main.dart
import 'package:first_app/page/home_page.dart';
import 'package:flutter/material.dart';

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

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

  // root Widget
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(), // 홈 페이지 호출
    );
  }
}

main.dart에서는 HomePage 위젯을 호출한다.

  • home_page.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  //사진 리스트
  final imageUrl = [
    "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/33053/dog-young-dog-small-dog-maltese.jpg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/2664417/pexels-photo-2664417.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/10361796/pexels-photo-10361796.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/9409823/pexels-photo-9409823.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
  ];

  final RefreshController _refreshController = RefreshController(); //새로고침 컨트롤러

//새로고침 기능 함수
  void _onRefresh() {
    setState(() {});
    _refreshController.refreshCompleted();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('14일차 과제 3번'),
      ),
      body: SmartRefresher(
        controller: _refreshController, //컨트롤러 연결
        enablePullDown: true, //아래로 당겨서 새로고침 활성화
        onRefresh: _onRefresh, //새로고침 이벤트 연결
        header: WaterDropHeader(), //새로고침 헤더
        child: Center(
          //테두리가 둥근 이미지
          child: ClipRRect(
            borderRadius: BorderRadius.circular(16),
            child: Image.network(imageUrl[Random().nextInt(imageUrl.length)]),
          ),
        ),
      ),
    );
  }
}

해당 화면은 새로고침 시 랜덤한 이미지가 다시 그려진다. 따라서 StatefulWidget으로 생성했다.

먼저 주어진 사진 리스트를 생성하고, RefreshController를 선언했다. 새로고침 기능을 수행하는 메소드는 _onRefresh()로 작성했는데 화면을 다시 그려주면 되므로 setState()를 사용했으며 딜레이는 따로 설정하지 않았다.

본문에서는 앱바로 타이틀을 출력하고, bodySmartRefresher 위젯을 사용했다. 해당 위젯에 앞에서 생성한 컨트롤러를 연결하고, 당겨서 새로고침을 활성화하도록 enablePullDown 속성을 true로 설정했다. 새로고침 이벤트인 onRefresh에는 앞에서 작성한 _onRefresh() 메소드를 연동하였다. header는 주어진 요구사항처럼 WaterDropHeader() 위젯을 사용했다.

child로는 테두리가 둥근 이미지를 ClipRRect를 사용해 생성하고 Center로 감싸 가운데에 그려주었다.

결과

4. “Slider, Carousel”를 주어진 패키지를 사용하여 만들기

아래의 지정된 패키지를 사용하여 결과물 예시와 같은 화면을 만들고자 한다.

  • 패키지 명
    carousel_slider

  • 결과물 예시
    • 결과물의 데이터는 과제 3번의 데이터를 활용할 수 있다.
    • 2.5초마다 다음 페이지로 자동으로 넝어가도록 설정하시오.

코드 작성

  • pubspec.yaml
dependencies:
	carousel_slider: ^4.2.1

dependenciescarousel_slider를 넣어 패키지를 설치했다.

  • main.dart
import 'package:first_app/page/home_page.dart';
import 'package:flutter/material.dart';

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

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

  // root Widget
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(), // 홈 페이지 호출
    );
  }
}

main.dart에서는 HomePage 위젯을 호출한다.

  • home_page.dart
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';

class HomePage extends StatelessWidget {
  HomePage({super.key});

  //사진 리스트
  final imageUrl = [
    "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/33053/dog-young-dog-small-dog-maltese.jpg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/2664417/pexels-photo-2664417.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/10361796/pexels-photo-10361796.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
    "https://images.pexels.com/photos/9409823/pexels-photo-9409823.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('14일차 과제 4번'),
      ),
      body: Center(
        child: CarouselSlider.builder(
          options: CarouselOptions(
            autoPlay: true, //자동 스크롤
            autoPlayInterval: Duration(milliseconds: 2500), //자동 스크롤 간격
            enableInfiniteScroll: false, //무한 스크롤 금지
          ),
          itemCount: imageUrl.length,
          itemBuilder: (context, index, pageViewIndex) {
            //둥근 모서리 이미지 리턴
            return ClipRRect(
              borderRadius: BorderRadius.circular(40),
              child: Image.network(imageUrl[index]),
            );
          },
        ),
      ),
    );
  }
}

해당 앱은 화면의 변화가 없기 때문에 StatelessWidget으로 만들었다.

과제 3번과 마찬가지로 우선 이미지 리스트를 선언했다. 본문에서는 CarouselSlider.builder()를 사용해 위젯을 만들었다. 해당 위젯의 옵션으로는 자동 스크롤을 허용하고 스크롤의 간격을 2.5초로 설정했다. 그리고 무한 스크롤은 금지했다.

PageView.builder()와 마찬가지로 itemCountimageUrl의 크기로 설정하고 itemBuilder에서 모서리가 둥근 이미지를 리턴했다.

결과


패키지 사용😮

다른 사람들이 올린 패키지를 가져다 사용해 보았다. UI가 이쁘고 복잡한 구현 없이 사용 가능하다는 점이 너무 좋았다. 내가 만드려는 앱의 UI에 맞게 적절한 패키지를 사용하면 개발 속도도 빨라지고 코드도 간결해져서 좋을 것 같다. 오늘 과제를 하면서 HTTP 헤더와 인증방법을 학습했는데 과제에서 원하는 내용을 잘 정리한 것이 맞는지 잘 모르겠다..ㅎㅎ 그것 보다 추가 내용 정리로 오늘은 학습을 하면서 나왔던 용어나 사용법 등을 정리했는데 되게 유용하게 사용할 수 있는 내용들을 정리한 것 같다ㅎㅎ (추가 내용 정리가 좀 많아서 레퍼런스도 많다 ㅋㅋㅋㅋ)

📄 Reference

profile
관우로그

0개의 댓글

관련 채용 정보