Flutter/Dart 에서 서버 응답 쿠키(Set-Cookie) 처리하기

Flutter/Dart 에서 서버 응답 쿠키 (Set-Cookie) 처리가 제가 생각하는 것 만큼 제대로 처리하는 녀석이 없어서 직접 간단히 파싱하는 방법을 공유해봅니다.

먼저 아래처럼 웹서버 HTTP 호출을 한다고 보면..

import 'dart:convert';
import 'package:http/http.dart' as http;

main () async {
  Map postData = {
    'access_token': '토큰값',
    'expires_in': 3600,
  };

  http.Response res = await http.post(
    Uri.parse('https://doogle.link/'),
      headers: <String, String> {
        'Content-Type': 'application/json', 
      },
      body: json.encode(postData),
    );
  print(res);
}

응답으로 받게되는 res 에는 headers[‘set-cookie’] 로 서버에서 보내는 응답 쿠키정보를 RAW 데이터로 확인할 수 있습니다.

하지만 제 실력부족으로 이를 제대로 파싱해주는 녀석을 pub.dev 에서 찾기 어렵더군요.

그래서 아래와 같이 RegExp와 dart:io 의 Cookie 클래스를 활용해 간단하게 RAW 쿠키데이터에서 완전하게 쿠키정보를 얻어올 수 있게 만들어 봤습니다. ^^

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

main () async {
  Map postData = {
    'access_token': '토큰값',
    'expires_in': 3600,
  };

  http.Response res = await http.post(
    Uri.parse('https://doogle.link/'),
      headers: <String, String> {
        'Content-Type': 'application/json', 
      },
      body: json.encode(postData),
    );

  // 여러개의 쿠키들을 분리해주는 Regex
  var exp = RegExp(r'((?:[^,]|, )+)'); 
  Iterable<RegExpMatch> matches = exp.allMatches(res.headers["set-cookie"]!);
  for (final m in matches) {
    // 쿠키 한개에 대한 디코딩 처리
    Cookie cookie = Cookie.fromSetCookieValue(m[0]!); 
    print('[set-cookie] name: ${cookie.name}, value: ${cookie.value}, expires: ${cookie.expires}, maxAge: ${cookie.maxAge}, secure: ${cookie.secure}, httpOnly: ${cookie.httpOnly}');
  } // for
} // main

다 좋은데 dart:io 는 DartPad 에서 지원하지 않네요. 또한 dart:io 의 Cookie 클래스는 sameSite 멤버를 가지고 있지 않아 이 값까지는 처리하지 못하는 문제점이 있습니다.

예제용 set-cookie Raw 데이터입니다.

cookieKey1=cookieValue1; expires=Fri, 28-Oct-2022 00:37:14 GMT; Max-Age=86400; path=/,cookieKey2=cookieValue2; path=/,cookieKey3=cookieValue3; expires=Fri, 27-Oct-2023 00:37:14 GMT; Max-Age=31536000; path=/; secure; httponly

위와 같이 set-cookie 값은 좀처럼 종잡기 힘든 형식으로 되어있습니다. ㅜㅜ

코드 테스트를 위해 응답부분을 주석처리하고 res.headers[“set-cookie”] 부분을 위 예제 문자열로 바꿔서 테스트 해볼 수 있습니다.

https://regex101.com/ 같은 regex 를 테스트 할 수 있는 곳에서 위 예제 데이터를 ‘((?:[^,]|, )+)’ 정규식을 가지고 파싱하면 아래와 같은 결과가 나옵니다.

Match1 0-85	cookieKey1=cookieValue1; expires=Fri, 28-Oct-2022 00:37:14 GMT; Max-Age=86400; path=/
Group1 0-85	cookieKey1=cookieValue1; expires=Fri, 28-Oct-2022 00:37:14 GMT; Max-Age=86400; path=/
Match2 86-117	cookieKey2=cookieValue2; path=/
Group2 86-117	cookieKey2=cookieValue2; path=/
Match3 118-224	cookieKey3=cookieValue3; expires=Fri, 27-Oct-2023 00:37:14 GMT; Max-Age=31536000; path=/; secure; httponly
Group3 118-224	cookieKey3=cookieValue3; expires=Fri, 27-Oct-2023 00:37:14 GMT; Max-Age=31536000; path=/; secure; httponly

각 match 결과를 Cookie.fromSetCookieValue() 메소드를 활용하면 기특하게도 제대로 분해해서 Cookie 객체를 만들어줍니다. ^^

최종 결과는 아래와 같습니다.

[set-cookie] name: cookieKey1, value: cookieValue1, expires: 2022-10-28 00:37:14.000Z, maxAge: 86400, secure: false, httpOnly: false
[set-cookie] name: cookieKey2, value: cookieValue2, expires: null, maxAge: null, secure: false, httpOnly: false
[set-cookie] name: cookieKey3, value: cookieValue3, expires: 2023-10-27 00:37:14.000Z, maxAge: 31536000, secure: true, httpOnly: true

[참고]