Skip to content

Instantly share code, notes, and snippets.

@JEEN
Last active August 29, 2015 14:11
Show Gist options
  • Save JEEN/454dea900ff6c828ce42 to your computer and use it in GitHub Desktop.
Save JEEN/454dea900ff6c828ce42 to your computer and use it in GitHub Desktop.
Boolean for Perl/JSON/etc

Boolean for Perl/JSON

펄로 몇 가지 제품을 개발하는 중 __다른 언어/환경과 데이터를 주고받고 하는 와중__에 회사 동료로부터

펄은 Boolean 표기가 없냐? 왜 데이터가 전부 0 아니면 1이야? C냐?

라는 이야기를 들었습니다.

그러고보면 그냥 익숙하게 true1, false0으로 사용하고 있었습니다.

아시다시피 펄의 기본 데이터 형에는 Boolean형이 없습니다. C도 마찬가지로 Boolean이 없지요. 하지만...

...
typedef enum {false, true} bool
...

위처럼 typedef로 얼추 그런 모양으로 내고 있는 장면을 몇 번 보기는 했습니다만...(아니, 그럼 펄에서도 enum 써서 하면 되는데)

어찌되었든 그러다보니 서두에서 말했던 것처럼 JSON 같은 형식으로 데이터를 주고받을 때는(예를 들어),

{
  "people": [
    {
      "id": 0,
      "name": "Jeen Lee",
      "is_drunken": 0,
      "has_iphone6": 1
    },
    ...
  ]
}

이런 식으로 is_* 같은 Boolean 데이터 형의 필드들에 0/1로 주는 만행을 벌여왔었습니다 으레 펄에서 사용하던 식으로 데이터를 넘겨줬었죠. 하지만 그때마다 Boolean 형을 지원하는 쪽에서는 이런 데이터들의 형을 일일이 변환해줘야 하는 수고가 있었습니다. 미안하고 또 미안했습니다.

나중에서야 이런 부분에 대한 배려가 부족했던 것을 반성하며 소잃고 외양간 고치기 격으로 알아본 바로는, JSON::* 류의 JSON 인코딩/디코딩에서 true/false를 넘기려면 아래처럼 \1, \0을 사용할 수 있습니다.

use JSON;

print JSON->new->encode({ is_jeen_drunken => \1 });   # {"is_jeen_drunken":true}
print JSON->new->encode({ is_jeen_homophobe => \0 }); # {"is_jeen_homophobe":false}

하지만 JSON의 Boolean 표기를 위해서 어떤 클래스의 접근자의 리턴값을 \1, \0으로 하는 것은 아래와 같은 문제가 있습니다.

if (\0) { print "No~"  }  # No~
if (\1) { print "Yes~" }  # Yes~

print \0; # SCALAR(0x7fc6b2803b78)

\0는 스칼라의 주소값을 가지고 있기 때문에 조건문에서는 _값이 있다_고 간주해버리기에 의도한 대로 되지 않습니다.

이런 문제를 해결하기 위해서 많은 JSON 모듈들은 use overload를 사용한 연산자 오버로딩으로 이 문제를 해결하고 있습니다. 그리고는 이런 표기법을 권장합니다.

use JSON;

print JSON->new->encode({ is_jeen_drunken => JSON->true }); # {"is_jeen_drunken":true}

print JSON->true;      # 1
print ref(JSON->true); # JSON::PP::Boolean
if (JSON->false) { print "No~" }  # No~ 라고 표시안됨

대개의 Boolean 형의 데이터를 반환하는 메서드라면 true라면 JSON->true라고 반환할까요?

package Person;
use JSON;

sub is_drunken { JSON->true }

...

왠지 이러면 모든 Boolean 형의 데이터들이 JSON과 깊은 관계가 있다고 생각해버리기도 하고해서 다른 방법을 알아보기로 했습니다. 단지 네이밍의 문제라 이거죠.

다른 대체제는 뭐가 있을까요?

아시다시피 펄은 그저 CPAN을 사용하기 위한 도구 CPAN을 사용해서 여러 위기상황을 해쳐나가고 있습니다.

아래처럼 boolean 모듈을 사용해서 true, false를 사용할 수 있습니다.

use boolean;

my $is_jeen_drunken    = true;
my $is_jeen_homophobe  = false;

print true;   # 1
print false;  # 2

if (true)  { print "True"  } # True
if (false) { print "False" }

-truth 옵션을 지정하면 ref()로 참조값까지 얻을 수 있습니다.

use boolean -truth;

print ref(0 == 1); # boolean
print ref(true);   # boolean

위의 JSON->true 같은 형태보다는 훨씬 더 깔끔합니다. 하지만 JSON 출력시에는 boolean도 좀 까탈스러운 과정을 거쳐야 합니다. 일반적인 JSON, JSON::XSconvert_blessed 옵션이 이 boolean에는 먹히지 않습니다(null을 반환합니다). 문서에서는 JSON::MaybeXS를 사용하는 것을 권장합니다(분명 convert_blessed 옵션으로는 TO_JSON을 호출할 텐데 왜 안될까에 대해서는 나중에 따로 알아보겠습니다).

다른 방법도 있습니다. 바로 Types::Serialiser를 사용하는 것이죠.

use JSON;  
use Types::Serialiser;  # JSON::XS가 설치되어 있는 경우는 `import`에서 처리되므로 use할 필요는 없음
print JSON->new->encode({ is_jeen_drunken => Types::Serialiser::true });

Boolean 형이 단지 JSON 만을 위한 것이 아니라는 것을 어필하는 것 같은 이름이지 않습니까? 사실 JSON::XS을 만든 _Marc Lehman_이 그렇게 생각했던 것같습니다. JSON뿐만이 아니라 CBOR 등에서도 사용할 수 있습니다.

그리고 조금 손이 가지만 MessagePack같은 것에서도 사용할 수 있습니다. Data::MessagePack 모듈의 경우도 Boolean 형에 대한 처리를 하고 있는 데, 여기에서는 Types::Serialiser로 정의한 true/false를 잘 맞출 수 있도록 관련된 부분을 사전에 변환해버립니다.

use Data::MessagePack;
use JSON::XS;

BEGIN {
    *Data::MessagePack::Boolean:: = *Types::Serialiser::Boolean::;
    *Data::MessagePack::true      = *Types::Serialiser::true;
    *Data::MessagePack::false     = *Types::Serialiser::false;
}

my $mp = Data::MessagePack->new;

my $data = { aaa => Types::Serialiser::true, bbb => Types::Serialiser::false };
my $r = $mp->pack($data);
print $r;   # ��aaaãbbb�
print length($r); # 11

my $u = $mp->unpack($r);
my $json = JSON::XS->new->encode($u);
print $json;         # {"bbb":false,"aaa":true}
print length($json); # 24

웹 개발에서야 JSON 위주로 데이터를 주고 받는 게 이제는 거의 상식처럼 받아들여지고 있습니다만, 사실 1바이트로 표현될 Boolean에 4~5바이트의 문자열이 오가는 상황이고... Javascript만 좋은 일 시키고 있습니다.

이거 시작은 Boolean으로 했는데, 마지막은 데이터 직렬화 이야기가 되어버렸네요. 앞에서도 언급한 대로 이 모든 것은 소 잃고 외양간 고치기 격입니다. 현재 진행중인 프로젝트에서 무턱대고 API의 응답결과를 저렇게 바꿔버리면 제품은 무너지고 저는 깨질테니까요.

지금까지는 Boolean불가론을 깨기위한 방법소개에 지나지 않았습니다만, 다음 기사(언제가 될 지 모르지만)에서는 이런 것들을 사용해서 펄의 객체지향 프로그래밍에서 어떻게 적용할 수 있을 지를 알아보고자 합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment