14일차 과제 - rlatkddbs99/Flutter GitHub Wiki

  1. HTTP요청을 보낼 때 사용되는 Query Parameter와 Header의 차이점을 정리하시오.

https://yuda.dev/250 참조

REST API의 파라미터에는 4가지 타입이 있다.

  • header 파라미터: 리퀘스트 헤더에 포함된 파라미터. 보통 인증 혹은 권한 부여에 관련되어 있다. header 파라미터는 보통 인증(Authentication; 특정 identity 제공)과 권한 부여(authorization; 특정 action 허용) 두 가지 목적으로 사용된다. read-only API의 경우엔 키가 필요 없는 경우도 있다. 하지만 대부분의 상업용 API는 API 키나 다른 메서드를 통한 허가를 필요로 한다. 만약 이런 보안이 없다면, 유저들은 별다른 등록 없이 API를 무제한으로 콜할 수 있을 것이고 이는 곧 수익 창출 문제를 야기 권한 부여(authorization)의 종류 1_API Key API 키는 리퀘스트 URL이나 리퀘스트 헤더에 포함되는 긴 문자열이다. API 키는 보통 API를 사용하는 사람을 식별하는 역할을 한다. 키의 종류에는 public 키와 private 키가 있다 2_Basic Auth 리퀘스트 헤더에 username:password 를 넣어 인증하는 방식이다. username과 password는 Base64로 인코딩된다. Basic Auth를 사용하는 API는 HTTPS를 사용하는데, 이는 즉 주고 받는 메시지가 HTTP 프로토콜 내에서 암호화 3_HMAC(Hash-based message authorization code) 4_OAuth 2.0

  • path 파라미터: 엔드포인트에서 쿼리문 이전의 파라미터. path 파라미터는 엔드포인트의 일부이다. 예를 들어, 아래의 엔드포인트에서 {user}와 {bicycleId}에 각각 그 값이 들어간다고 보면된다. /service/myresource/user/{user}/bicycles/{bicycleId}

  • query string 파라미터: 쿼리문 내의 파라미터. 엔드포인트가 끝난 뒤 물음표 뒤에 온다. 엔드포인트에서 물음표(?) 뒤에 등장하는 query 파라미터는 다음과 같은 형식을 지닌다. /surfreport?days=3&units=metric&time=1400 가끔 path 파라미터와 query 파라미터 중 무엇을 사용할지 고민하곤 하는데, REST API의 모범을 준수하자면 path 파라미터는 특정 리소스를 정의할 필요가 있을 때, query 파라미터는 정렬 혹은 필터링이 필요할 때 사용한다.

  • request body 파라미터: 리퀘스트 바디에 포함된 파라미터. 보통 JSON 형식으로 제출된다. 보통 POST 리퀘스트에서는 JSON 오브젝트를 리퀘스트 바디 안에 넣어 보낸다. 이것이 바로 request body 파라미터이며 주로 JSON으로 되어 있다.

  1. 누구나 요청할 수 있는 HTTP 통신에 응답값을 인증된 사용자 혹은 로그인 된 사용자에게만 전달할 수 있는 방법에 대해 알아보고 정리하시오. (또는 방법을 제안하여도 됨) -> https://velog.io/@rmfrn2901/HTTP-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%8C%80%ED%95%B4-%EC%84%A4%EB%AA%85

인증 방식의 4가지 1.basic 인증, 2.digest 인증, 3.ssl 클라이언트 인증, 4.폼 베이스 인증

image

basic 인증 기본 인증 방식으로 클라이언트에 대한 사용자 이름과 암호를 base64로 인코딩 문자열을 보낸다. 보편적으로 사용하는 방식이지만 스니핑 공격에 취약하다. 따라서 보안적인 측면에서는 다른 인증 방식을 사용하는 것이 좋다.

image

2.digest 인증 사용자명, 패스워드 등을 조합하여 md5 값으로 인증한다. 기본 인증보다 보안이 강화된 인증 방식이다. 동작원리는 클라이언트에서 페이지를 요청하면 서버는 digest 인증이 필요하다고 통보하고 클라이언트는 사용자 아이디, 패스워드를 입력하면 서버+클라이언트 정보 병합 후 md5로 암호화 시켜 전송한다. MD5란 Messages-Digest algorithm5 약자로서 128비트 암호화 해시 함수이다. 주로 무결성 검사 등에 사용된다.

image

3.ssl 클라이언트 인증 HTTPS의 클라이언트 인증서를 이용한 인증 방식으로서 단독으로 사용되지 않고 폼 베이스 인증과 합쳐져서 이용되고 있다. 인증이 필요한 리소스의 리퀘스트가 있을 경우 서버는 클라이언트에게 클라이언트 증명서를 요청하고 사용자는 클라이언트 증명서를 선택 후 서버 송신하고 서버는 클라이언트의 증명서를 검증하여 검증 결과가 정확하다면 클라이언트의 공개키를 취득 후 보안 세션이 이루어진다.

image

4.폼 베이스 인증 basic 인증이나 digest 인증은 사용상의 문제와 보안적인 문제로 사용이 되지 않고, ssl 클라이언트 인증도 도입 비용이나 운용 비용의 문제로 널리 사용하지 못하고 있다. 폼 베이스 인증 방식은 http 프로토콜로서 표준 사양이 결정되어 있지 않으나 일반적으로 자주 사용되고 있는 방법으로는 세션 관리를 위해 쿠키를 사용하는 방법 클라이언트와 서버에 있는 유저 ID나 패스워드 등의 자격 정보를 포함한 리퀘스트를 송신한다. 보통은 POST 메소드가 사용되어 엔티티 바디에 자격 정보를 저장한다. 서버 측은 유저를 식별하기 위해서 세션 ID를 발행한다. 클라이언트에서 수신한 자격 정보를 검증하는 것으로 인증을 하고, 그 유저의 인증 상태를 세션 ID와 연관 지어 서버 측에 기록한다. 클라이언트 측에 송신할 때는 SET-COOKIE 헤더 필드에 세션 ID를 저장해서 리스폰스를 반환한다. 서버 측에서 세션 ID를 받은 클라이언트는 쿠키로 저장한다. 다음번에 서버에 리퀘스트를 송신하는 때에는 브라우저가 자동으로 쿠키를 송출하기 때문에 세션 ID가 서버로 송신되면서 인증이 된다.

pull_to_refresh -> https://dkswnkk.tistory.com/377 list ranom -> https://stackoverflow.com/questions/17476718/how-do-get-a-random-element-from-a-list-in-dart

캡처3

main.dart

import 'package:dev/HomePage.dart';
import 'package:flutter/material.dart';

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

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

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

HomePage.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

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

  @override
  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();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("14일차 과제 3번"),
        centerTitle: true,
      ),
      body: SmartRefresher(
        controller: _refreshController,
        enablePullDown: true,
        onRefresh: _onRefresh,
        header: WaterDropHeader(),
        child: Center(
          child: ClipRRect(
            borderRadius: BorderRadius.circular(12),
            child: Image.network(imageUrl[Random().nextInt(imageUrl.length)]),
          ),
        ),
      ),
    );
  }
}

CarouselSlider -> https://black-glasses.tistory.com/11

캡처2

HomePage.dart

import 'dart:math';

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

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

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

class _HomePageState extends State<HomePage> {
  final List<String> 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();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("14일차 과제 4번"),
          centerTitle: true,
        ),
        body: Center(
          child: CarouselSlider(
            options: CarouselOptions(
                autoPlay: true, autoPlayInterval: Duration(milliseconds: 2500)),
            items: imageUrl
                .map((e) => Container(
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(16),
                        child: Image.network(
                          e,
                          fit: BoxFit.cover,
                          width: 150,
                        ),
                      ),
                    ))
                .toList(),
          ),
        ));
  }
}
⚠️ **GitHub.com Fallback** ⚠️