늘모자란

늘모자란


레이드.

마비노기라는 게임에 레이드라는 단어가 이제 어울릴지 모르겠습니다.
유저 하나하나가 너무 강해지는 바람에 체력이 못따라가는 상황인 것 같더군요..



그럼에도 불구하고 싴갤러스에서는 유저분들의 제보를 이용해 보다 더 나은 레이드 환경을 만들려고 항상 노력하고 있습니다 (진짜)
오늘 이 글에서 다룰 이야기는 바로 그 레이드 개선에 관한 이야기입니다

구조적 문제를 안고 시작하다


처음 싴갤러스의 레이드를 다룰때는 네이드, 거대사자, 거대악어, 평원 드래곤 그리고 이프리트 5종만 다루었습니다.



아마 위 레이아웃은 본적도 없으신분들이 많을 것 같은데요.
이때는 웹, 즉 PHP를 막 배우고 있을때 만든 홈페이지라 레이드 별로 페이지를 전부 하나씩 생성했습니다.
그리고 DB에서 보스별로 불러와 처리했죠. 그러니까 즉, SQL 쿼리만 살짝 다른 페이지가 5종, 전체를 볼 수 있는 페이지까지 해서 6종이죠.
그리고 자바스크립트도 잘 할 줄 몰라서 하나의 페이지를 가지고 인자를 전달해 다른 결과를 보여준다는건 생각도 못했고, 무조건 페이지 이동이 필요했습니다


js를 좀 배우고 나서도 문제가 해결되진 않았습니다.
이미 나눠진 페이지를 굳이 합칠 필요가 없었기 때문입니다. 그리고 이땐 또 ajax와 REST개념을 잘못 이해하고 있어서 ajax로 데이터를 불러오는게 아니라 페이지 자체를 그대로 불러와 대상 dom 에 씌우는 식이었습니다. 그러니까, 불러오는 페이지에서 표까지 완전히 작성하는 구조였죠.

그렇게 많은 페이지들이 만들어진 상태에서 레이드가 계속 늘어났습니다. 레드 드래곤부터 시작해서, 시간이 고정이었던 사막 드래곤, 신규 레이드인 블랙, 화이트 드래곤부터 지금의 실반 드래곤, 모쿠르칼피까지. 첫 단추를 잘못꿴 상태였기때문에 똑같은 페이지를 계속 만들어낼 수 밖에 없었습니다. 잘못된건 아는데, 빠르게 결과물을 내야했기때문에 -당시에 레이드는 경쟁구도의 사이트가 있었습니다- 속도를 올릴 수 밖에 없었고, 나중을 기약하며 조악한 페이지들을 계속 늘려만 갔죠. 그 결과는...


(여기서 strings는 작업하려고 추가해놓은거고 실제론 없었음)

raid3.php 이라는 뷰 페이지에서 page를 include 해서 보여주는 식이었고, 각 페이지에서는 각 프로세스를 처리할 (레이드 마다 시간이 다르기때문에) 페이지에 1:1로 매칭이 됩니다. 초록색은 작업하려고 만든거라 상관없고... 그러니까, 거의 20여개에 달하는 페이지로 구성되어 있던거죠... 그리고 스크립트도 PHP 변수를 사용해 처리하기때문에 인라인 스크립트로 작성되어 있고, css도 인라인 css로 작성되어 있기때문에...

단 하나의 수정이 발생하면 최소 8개 페이지(레이드가 다르더라도 인터페이스는 공통된 경험을 줘야하니까)가 수정되어야했고, 좀 심하면 처리 페이지까지 수정해야했으니 1글자를 고치기 위해 20페이지가 수정되어야 하는 기염을 토하기도 했습니다.
구조적인 문제가 분명히 있었으나, 다들 잘 쓰고 있으니까요. 굳이 수정이 안되어도 문제가 없었습니다. 어차피 레이드는 완성형이라고 생각했고, 뭐 하나 추가되면 페이지 늘려야지, 이젠 도가 텄으니 빨리빨리 수정할 수 있다 이런식으로 애써 신경안쓰려고 하고 살았습니다..


곪은건 터지게 된다


신규 기능 요청이 들어왔습니다. 별건아니고, 전광판을 여러개 띄울 수 있게 해주세요.... 인데
모든 보스몬스터가 별도로 구분되어 있기때문에 현재의 구조로는 제공할 수가 없었습니다. 정확히는 가능은 하지만 불필요한 함수를 작성해야할 수도 있고, 뭣보다 중간에 수정이 일어나게 되면 모든 페이지에 같은 내용으로 또 작성... 아... 더 이상은 얘기하기 싫네요.

그래서 이왕 이렇게 된거 레이드 자체를 다시짜자고 결심하게 되었습니다.
단 한번의 수정으로 많은 경험을 선사해주자는거죠. 결론부터 작성해보자면 다음과 같이 변했습니다.
여기에 raid.js 라는 별도로 분리된 js 파일이 하나 더 있습니다. 뷰를 담당하는 파일이 하나 더 있고요.



먼저 페이지를 통일하는게 중요했습니다.
어차피 설명만 좀 다르고 마크업은 똑같기 때문에 (아주 약간만 다르던지) assets에 strings 를 추가해 파라미터별로 설명을 다르게 표시해주는것부터 시작했습니다. 같은 이름의 변수를 case마다 계속 작성해나가는게 유쾌한 경험은 아니었지만 필요한 작업이니까, 좋게 말하면 string을 분리한거고, 나쁘게 말하면 우겨넣은거죠. 그래도 다음과 같아졌습니다.



이제 이 수많은 print_보스들... 왜 이럴까 소스를보니 초반부는 다른데 후반부는 완전 같습니다.
이상하네 하고 보니까(내가 짠건데 이상하네라고...) 그냥 시간을 체크해서 레이드 시간인지 아니만 판단하는 부분만 다를뿐 모두 같더군요.
그리고 이상하게 뿔피리데이터를 이쪽에서 만들고 있었는데 검색 키워드만 조금씩 다르고 쿼리도 같고요. 자갈치 시장도 아니고 한번에 처리하려고 발악을 한 느낌이었습니다. (print_smd_data , print_smd_horn 이렇게 안하려고...?)

해야할건 명확했습니다. 각 보스별로 시간 체크 및 시간, 제보 정보를 합치고, 뿔피리 데이터를 분리해 처리하는 두개파일로 나누는거죠.
그리고 전광판을 여러개 띄웠을때도 같은 페이지로 온전히 동작해야하니까 범용성을 살짝 넣고.

그렇게 서버에서 주는 값들을 대강 명세하고, js 를 수정했습니다.
기존엔 js안에 서버 변수를 넣어서 그대로 작성한 케이스가 많았는데 이번엔 어떤 서버변수도 사용하면 안되었기때문에, url 을 이용해 구분했습니다. 그리고 대부분의 DOM을 dynamic 하게 만들어야했기때문에 js안에 html div를 많이 작성했습니다. 이게 맞는 방법인진 모르겠는데.. 맞겠죠?;

js를 작성하면서 느낀건데 multi line을 작성할때 쓰는 ` (back qoute)와 new URL, filter arrow 등은 IE에서 사용할 수가 없었어서 다 만들었다 생각했는데 대체하기 위해 다시 작업을 하고.... 뭐 그랬던거 같네요. 그리고 신규 관리자분이 들어왔는데... 시련이 시작되었습니다. css 지옥이었는데.. 봐주는 관리자 한명이 잠을 안자...




화면을 같이 보고 있는게 아니라서 캡쳐 후 보여주기를 계속 반복해야했고 결국 대화방 앨범이 이거 관련 내용으로 한페이지를 넘게 차지하게 되더군요. 개인적으로 디자인 작업은 안좋아하기때문에 아주 많이 힘든 시간이었습니다.... ㅠㅠ
신규 기능 추가를 마치고, 테스트 서버에서 기능이 실제 데이터 기반으로 잘 돌아가는걸 확인하고 기능을 릴리즈 했지만 IE 호환문제, 창이 자동으로 안뜬다던지, 채팅 제보 정규식에 문제가 있다던지 하는 소소하면서도 안되면 불편한 문제들이 계속 발견되면서, 한번에 완벽하긴 정말 어렵구나 하는 생각을 했습니다

돌아보며


금번 작업은 보기엔 별거 없습니다. 기능한개가 추가 되었고, 사이트 외형적으로도 거의 변한게 없습니다.




그러나,
5,000여 라인의 코드 삭제가 있었고, 2,200 여 라인의 코드 작성이 있었습니다.. 이 작업은 단 이틀만에 수행되었고요..
훨씬 많을줄 알았는데 이거밖에 안돼 같은 생각이 들지만, 범용성을 위한 코드 리팩토링은 싴갤러스를 운영하면서 처음인거같습니다.

코드자체가 대단한것도 아니고 난이도가 있는 코드도 아니지만, 짧은 시간내에 끝내려고 일정을 빡빡하게 잡았고, 오히려 시간이 타이트했기때문에 한줄한줄 쓸모없이 작성하지 말자 이렇게 임했던 느낌입니다. 유저 수요도 고려하고, 중간에 기획을 바꾸기도 하면서..
개인적인 성취를 이룬 느낌이 더 강한 업데이트였습니다. 구조 통일도 했으니 앞으로 레이드쪽에 더 많은 기능 추가가 있을....수도 있겠죠?

다음엔 더 재밌는 글로 찾아뵙겠습니다~
2018/09/17 09:43 2018/09/17 09:43

앱 업데이트는 매번 참 두근거리는 일입니다. 
검수를 받기때문에 웹 처럼 빠르게 업데이트가 불가능하고(상대적으로), 기기의 이상 유무, 앱 설치와 제거 여부등 보고 있자면 흥미로운 통계가 많기 때문입니다.

사실, 싴갤러스는 모바일 레이아웃을 지원하기때문에 네이티브로 앱을 구성할 필요가 없고, 굳이 쓴다면 푸시 기능때문에 앱을 존속시키고 있는 것인데요. 그래서 그런가 많은 공수를 들이고 있지 않은것도 사실입니다. 그래도 이번에는 아이콘 그림도 많이 교체했고, 자주 볼 수 없는 화면에 신경도 (...) 썼습니다. 예를 들면 서버를 선택하는 화면에서 UI의 색이 바뀐다던지하는...

기본 웹에서 지원하는 아래로 끌어서 새로고침하는 기능도 넣어보았습니다. 이젠 당연하게 여겨지는 기능이고..
또, 안드로이드에서 갑자기 네트워크 문제가 발생했을경우 에러 다이얼로그 폭탄이 나오는 문제들도 수정했고, 캐시가 계속 쌓여서 앱이 비대해지지 않도록 신경도 써봤습니다.

그외에도 뭐 ... 다 말하지 못하는 사소한 것들도 참 많습니다만 어쨌든, 싴갤러스 1.13 앱을 즐겨주세요!
2018/09/11 09:14 2018/09/11 09:14
사건 발생
지난 10월 25일 0시경, 사이트가 이상하다는 말을 듣게 되었습니다.
확인해보니 MySQL 서버가 말썽, 근데 좀 이상합니다. 잘될땐 잘되는데 새로고침할때마다 DB가 에러를 throw하고 있는데 왜지..?
가끔은 no such file or directory도 뜨고, too many connections도 뜨고.. 그리고 gone a way도 뜨는 등 난리도 아닙니다. 도대체 무슨일이 일어난걸까요.

서버 다운
처음 의심했던것은 MySQL 을 5.7.20으로 업그레이드 했기 때문 아닌가였습니다. 그런데 업그레이드는 아침에 이뤄졌고, 오류가 난건 거의 15시간이 지난 이후.. 연관이 없어보입니다. 아직도 업그레이드 했기때문에 터진게 아닌가 하는 강력한 의심을 가지고 있지만, 어쨌든 사건에 집중해봅시다.

제일 먼저 사이트를 활성화 시켜야했습니다. 뭐 할때마다 에러가 나는데 되는것도 아니고 안되는것도 아닌 기괴한 상태가 지속되고 있었습니다. 쉘에서 MySQL에 붙으려고 해도 too many connections로 붙을 수가 없을정도로 이미 점거 당한 상태. 보나마나 process에 행이 걸려서 뒤의 요청을 처리 못하고 있는 것이겠지요. 도저히 확인을 할 수 없을정도로 밀려들어오니 일단 웹서버를 내렸습니다. (어차피 서비스가 정상작동하고 있지도 않았습니다)

이제 살펴보자
서버를 내린 이상 시간끌리지 않고 최대한 빠르게 문제를 확인해야 했는데 에러로그를 보니 메모리 관련해서 에러가 찍혀 있었습니다. key buffer size를 조절하라.. 뭐 memory trap 이라고 적힌거도 있고, because you hit a bug 이런식으로도 쓰여있는데 아니... 그래서 뭐가 문제라는건데... 정작 유추할 수 있는 에러로그는 없고 미칠 노릇입니다. 친구말로는 메타데이터 파일이나, 권한 문제가 일어났을 수 있다고 하던데 (바로 며칠전에 비슷한 에러를 겪음) 문제도 없었습니다. 

일단 뭐가 깨져도 깨진건 확실합니다. MySQL에서는 innodb 엔진을 쓰는 경우 1~6 레벨(?)까지 강제 복구 옵션을 지원하고 있습니다. recovery 레벨을 설정하면 read-only가 되는데 이때 살릴 수 있는거라도 살리려면 mysqldump해주는게 좋습니다. 저도 데이터를 일단 백업해야겠다는 생각에 mysqldump로 DB를 하나씩 덤프뜨긴했는데 특정 DB에 접근하니 바로 dump가 중지됩니다. select를 못한다는거죠. 어떤 DB가 깨졌구나하고 대강 범위를 좁힐 수 있었습니다.

2017-10-25T01:35:15.821493+09:00 4 [ERROR] InnoDB: Failed to find tablespace for table [ ] in the cache. Attempting to load the tablespace with space id 23958


왜 범위를 좁혔냐 하면, 위 로그는 전혀 상관없는 DB에서 났기때문입니다. 아마 하나가 깨지니 뒤에 따라오는 테이블을 모두 읽지 못하는 현상이 아닌가 싶은데.. 범위를 줄여나가는게 중요합니다.

그다음에는 .ibd 및 .frm을 모두 cp한후에, 문제가 일어났을것이라 생각하는 db의 내용을 모두 지우고, MySQL을 재시작해 싹 밀고, 다시 붙여넣어봤습니다. 해당 DB가 없으니 기가막히게 잘 굴러갑니다만 다시 데이터를 넣자마자 바로 db가 재시작되어버립니다. 여기서 ibdata가 박살이 났구나하고 감을 잡을 수 있었습니다.
보통은 이럴때 mysqlfrm을 사용해 테이블 구조를 새로 만들고, 데이터를 다시 밀어넣는식으로 복구합니다만, 저 같은 경우는 테이블 수도 많고, 일단 db에 select를 하면 db가 재시작되어버리기때문에 별 의미가 없다 생각했습니다. (사이사이 frm및 ibd 데이터를 하나씩 넣어서 산 테이블을 확보해보려했으나 거의 소용없는 짓이었습니다). 메모리 에러 일 수 있을것이라 생각해 서버 재부팅도 두번이나 했으나.. .안되네요.

결국 여기서 손을 들었습니다. ibd와 frm이 있지만 애초에 붙어지질 않으니 복구할 수가 없구나. 마지막 백업이 언제지? 아침 6시입니다. 그리고 새벽까지 삽질하고 있었으니 그 동안에 누적되지 못한 로그들도 많으니 문제가 된 DB는 최대 24시간정도의 데이터 유실이 발생합니다. 사이트는 다시 돌지만, 뼈아픈 경험입니다. 24시간마다 백업이 돌고 있으나, 유실될 양을 생각해보면 터무니 없이 부족합니다. 어떻게 해야할까요?

무중단 백업은 어떻게?
4년전 쯤에서는 MySQL의 replication을 활용하고 있었습니다. 이때는 DB쿼리가 최적화 되어있지 않아 쿼리한개가 DB를 잡아먹던 시절이기도 하고, VPS에서 사용했기때문에 서버 사양이 워낙 안좋아 시도때도 없이 DB가 죽을때가 있었습니다. 정확히 다시 말하면, 백업이 아니라 cpu 와 메모리 사용량을 분산시키기 위한 cluster개념으로 replication을 사용했습니다.

그러나 세월이 꽤 흘렀죠? 문제가 된 DB 쿼리도 다 수정되었고, 서버 스펙은 그때와 말도 안되게 뻥튀기되었습니다. 서버 두대를 돌릴 이유가 없으니 자연스럽게 replication 서버를 내렸었죠. 이 얘길 왜 하느냐? 지금부터 replication을 사용할 것이기 때문입니다.

왜? 기존에 사용하고 있는 mysqldump는 모든 db를 full select 합니다. 또한 쿼리 캐시 옵션을 비활성화하기때문에 DB에 심각한 부하가 발생하며, 사이트 전체의 성능저하도 일어납니다. 따라서 백업텀을 줄이면 안되겠느냐 같은 말은 사용량이 많은 시간대엔 절대 먹히지 않는 말입니다. 고로, 부하가 발생하지 않으면서도, 실시간 백업을 할 수 있는 방법 = 데이터가 insert되면 다른 데이터베이스에 저장하는 방법은 뭐가 있죠? replication입니다. 그래서 docker를 이용해 가상 서버를 하나 만드는걸로 진행합니다.

다음과 같이 변경했습니다
모든 과정이 매끄럽게 진행되진 않았지만, 주말동안 작업해 적용된 내용은,
Replication 서버를 돌려서 한쪽 DB가 맛이 가더라도 관리자 확인하에 즉시 DB를 변경할 수 있게 변경했습니다.
서비스 중일때 발생하는 dump 백업의 텀을 3시간으로 줄였습니다.(기존 24시간)
마지막으로, 서버 이미지를 하루마다 저장해 허브에 푸시합니다. DB 두개가 한번에 박살나도, 최대 10분내에 복구가 가능합니다.(순수하게 다운로드 시간)

얻은 결론
서버 용량이 풍족하진 않습니다. 이번 구성으로 인해 꽤 많은 용량을 차지하게 되었습니다만... 서버 용량이야 좀 더 자주 확인하면 되는 것이고, 밤을 안새게 해주진 않습니다. 또한 사용자 여러분들의 사이트 사용에도 차질이 없도록 만반의 준비를 했어야 했는데, 진작해야할 일을 이제야 한 것 같습니다. 좋은 교훈을 얻었다 생각하고, 서버 안정화에 더 힘쓰겠습니다
2017/10/26 10:02 2017/10/26 10:02
마비노기라는 게임이 다른 게임과 차별화되는 점이라면 바로 색이 아닐까요?
어떤 아이템이던 유저들이 원하는 색으로 커스터마이즈 할 수 있다는 점은 게임 출시 직후나 지금까지도 신선한 시스템임에 틀림없습니다.

자유롭지 않은 색상

아시겠지만, 마비노기의 색상 선택은 완전히 자유롭진 못합니다.
게임 운영진이 정한 팔레트내에서 염색이 되기 때문입니다. 이렇게 미리 정의된 색상들은 착용할 수 있는 모든 부위에 존재합니다.
(몬스터 드랍의 경우는 다른 팔레트를 사용합니다. 때문에 오리지널 색상에만 있는 색도 존재합니다)

외국 마비노기 커뮤니티에 잘 쓰여진 Mabination의 글이 있어 소개하겠습니다.
우리에게 익숙한 다음과 같은 팔레트가 있습니다

천옷 염색 팔레트

이런 팔레트는 어떻게 매번 인터페이스를 열때마다 다르게 생성되는것일까요?
팔레트들의 원형이 되는 이미지가 아이템 부위별로 존재합니다.

천옷 팔레트의 raw data

미리 정의된 색상이 있고, 이를 다음 연산을 통해 비틉니다

팔레트 생성 그래프


이를 똑같이 생성해내는 wrapper 가 있으나 이것까지 알 필욘 없고..
(사실 잘 모르겠습니다. 모의 염색 시뮬 만드는 날이 올 수도 있지 않을까요?)

저희가 주목한 사실은 팔레트의 raw data가 미리 정해져있다는 것입니다. 즉, 한 픽셀씩 이동하며 색을 추출해내면(혹은 라이브러리를 이용), 일반 앰플로 염색할 수 있는 색상을 추출해낼 수 있을 것이라 생각해 작업을 시작했고, 각 타입별로 다음과 같은 유니크한 색들이 존재함을 알 수 있었습니다.

타입 갯수
천옷 13,536
야광 39,452
가죽 9,975
실크 9,711
금속 7,858

더 많은 파트를 확보할 수도 있었으나, 가장 많이 사용되는 파트를 뽑았습니다. 그 마저도 요새 나오는 아이템들은 전부 천 파트라 야광과 실크를 할까 말까 고민을 많이 하기도 했는데요. 그냥 다 넣는걸로 했습니다.

이제 붙일 내용은 준비가 끝났습니다

처음에는 이렇게 만들려고 했습니다



제일 처음에는 앞서 설명한 내용만을 가지고 구성했습니다.
그런데 보기가 너무 불편해서 다음과 같이 그림으로 나타내볼 생각을 했습니다.


그러다가 문장 하나를 적었는데 비슷한 색.. 비슷한 색.. 이 블로그에도 내용이 있지만 color difference를 가지고 놀아본적이 있기때문에 충분히 뽑아줄 수 있을 것이라 생각했죠. 그래서 이 기능을 서브 기능이 아니라 본 기능으로 빼자, 넓게넓게 보여주고 지염에서도 비슷한 거도 보여주고 넓게 넓게 탭을 구성하자고 생각했고 다음과 같이 구성했습니다.


이 후에, 출처들도 누르면 검색되게 하고 글자 색도 배경에 따라 바뀌게 하는 등 변경을 했는데, 큰 변화 없이 구성을 했습니다.
여기서 UI/UX 적 고민을 하게 되었는데 과연 표시된 색을 클릭할 생각이 드냐는거였습니다. 누가 도대체 이런 색 상자 박스를 클릭할 것인가에 대한 토론이 있었고, 아이콘을 넣어보자는 의견이 있었습니다.

그리고, 여태까지 진행된 추가 검색은 모두 있는 색 상자에 대해서만 진행되었던 것으로, 완전히 존재하지 않는 색이 검색되더라도 해당 기능을 제공하기로 결정했습니다.



그래서 이런 모양을 모든 색 상자에 적용해 다음과 같은 최종 결과물로 완성하게 되었습니다.


근접 색상 검색에 대하여

그간 썼던 글을 읽으셨던 분들도 있겠지만, 싴갤러스에서 근접 색상 정렬을 위해 사용하는 기법은 CIE 2000 입니다. 헌데 색상 거리 검색이라는건 사실 이거다! 하고 명확하게 정의할 수가 없습니다. 사람이 느끼는것과 기계적(이론적)으로 판단하는것과는 꽤나 차이가 있기 때문입니다.

CIE 2000 으로 색상을 정렬했을때 다음과 같은 문제가 있었는데요.


리블 검색을 했을때 근접해서 나오는 색상에 왠 이상한 흰색이 있다는 것이었습니다. 헌데 색을 군집해서 검색하는 CIE 2000 (다른 CIE들도 비슷) 특성상, 잘못된 결과가 아니었습니다. 검정색을 검색했는데.. 흰색이 가장 근접한 색상 BEST 10 이라고 보여드릴 수는 없었습니다. 따라서 tie-break (CIE로 색을 검색할 경우 소숫점 5자리 단위에 따라서 갈라지는 경우도 많습니다)를 위해 추가 weight를 주기로 했습니다.

혹시 CIE 2000이 잘못구현된게 아닌가 싶어서 여러 언어로도 해보고, 직접 wrap을 해보기도 했는데 할때마다 맞다는 결과만..

색의 거리를 검사하는 다음 공식으로 보여줄까도 생각했지만 터무니 없는 결과만 나왔구요.

sqrt((r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2)


그래서 찾아다니고 실험해보다가 가장 거리가 많이 나오는 다음 공식을 추가 weight으로 결정했습니다. (최종 값은 *0.1)
이 글을 읽고 적용한건데 잘못된 선택이 아니었길 바래봅니다..

double ColourDistance(RGB e1, RGB e2) {
    long rmean = ( (long)e1.r + (long)e2.r ) / 2;
    long r = (long)e1.r - (long)e2.r;
    long g = (long)e1.g - (long)e2.g;
    long b = (long)e1.b - (long)e2.b;
    return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8)); 
}

지염 도서관은 게임 시스템상 많은 수요가 있다고 생각합니다. 앞으로 또 어떤 삽질로 만나 뵙게 될지 기대 되네요.

ps. 기능을 만들던 와중에...







2017/07/20 11:03 2017/07/20 11:03


싴갤러스 - 하나의 기능 리뉴얼을 준비하며 (1) 로부터 이어집니다


게임 아이템 거래에 대한 개념을 짚어볼 필요가 있었습니다.

1. 게시판
대부분의 거래사이트에서는 게시판 레이아웃을 채택하고 있습니다.
왜냐면, 구축이 쉽고 빠르기 때문입니다. 게시글이란것은 글 내용을 눌러보기 전까지 내용을 알 수없습니다. 검색을 하지 않는다면요.
게시판 레이아웃으로 구성되어 있는 홈페이지를 사용하는 유저들은 다음과 같은 흐름대로 움직입니다.

1. 게시판 접속
2. 서버 게시판 클릭
3. 제목을 하나씩 보며 페이지를 넘기다가 클릭
3-1. 검색을 통해 원하는 내용을 검색후 제목을 클릭

유저는 어떨때 글을 클릭하는것일까요? 저는 그것이 바로 제목이 가지는 힘이라고 생각했습니다.
예를 들어 다음과 같은 제목들을 말합니다.

'여러 의장 아이템 팝니다.','의장아이템 많이 팝니다.', '싸게 처분합니다' 등으로 흥미를 유발하는것이죠.
실제 필요하지 않은 아이템이라고해도 눌러보거나, 어떤 아이템이 있나 보거나... 이 케이스로 인해 발생하는 경우는 '뜬금없이 싸거나, 원하는 옵션보다 쌌을때'와 같이 조금 특별한 경우에만 해당합니다.

2. 정보 소모 속도가 빠르다
그냥 글을 다 읽어보는 부류도 있습니다. (괜찮으면 구매해볼까~ 같은 마인드). 비슷하게도, 역시 별로 좋아보이지 않는 아이템은 휙휙 넘기기때문에 정보의 소모가 매우 빠르게 이루어진다고 할 수 있습니다. 하지만, 게시판 레이아웃대로 구성시 제목을 보고, 클릭하고, 맘에 드는 아이템이 없으면 다시 뒤로가기를 통해 목록을 조회해야하므로 최소 2번의 리프레쉬가 일어납니다.

그래서 모티브를 '한눈에 볼 수 있고, 최소한의 새로고침'으로 잡고 닿은 결론이 METRO UI였습니다.
METRO UI는 사각형들이 다닥다닥 달라붙어 잇는 형태인데, 이를 응용해 갤러리 형태로 만들면 시야에 들어오는 글은 오히려 게시판 보다 더 많지 않을까라는 생각을 하게되었습니다. 또한 작성하는 아이템을 무조건 한개만 작성하게 강제한다면, 오히려 가독성면에서 더 좋지 않을까요?



이런 형태이고, 네이버 게시글이 16개정도가 한페이지인것을 고려하면, 비슷한 내용으로 한페이지를 사용한다 할 수 있습니다.


요렇게만 나열되면 좋겠지만, 다음 그림과 같이 무작위아이템들이 올라올 경우 산만해질 수 있다는 의견이 있었고


무작위로 나열될때 격자가 아닌 이상한 형태의 모양이 만들어져 오히려 글자를 읽는게 불편한 느낌이 들었습니다. 결국 이를 보완하기 위해 여러 안을 내야 했습니다.

고민하던 와중에, 이 글을 보게 되었는데, 핀터레스트에서 사용하고 있는 카드형식의 UI를 사용하면 어떨까 했습니다.
역시 기본 아이디어는 비슷하지만, 개체 하나하나마다 변별력을 주자는것이 핵심 아이디어였습니다.

Masonry를 사용할수도 있었지만, 원하는 모양대로 잘 안나오더군요.

그리고 무한히 나오는 결과물에 대해서도 테스트를 진행해봤으나, 제대로 정렬이 잘 되지 않았습니다.
네이버의 eg를 쓰는것은 처음엔 별로 내키지 않았지만(용량문제도 있지만, 어차피 jquery를 쓰니까...), 일단 적용해보기로 했는데 생각보다 결과물이 예쁘게 나왔습니다. 사용하기도 쉽고 (세팅에 많은 시간을 필요로 하지 않음)......


뼈대밖에 없긴한데, 이만하면 원하는대로 모습이었습니다..
적용을 하면 다음 모양처럼 나옵니다. 다만 갤러리형태에 적합했기때문에 모양새가 조금은 마음에 안들었습니다. 정확히 responsive 되는것도 아니었구요. 조금 아쉬운 상황이었습니다.




여기까지 준비를 했는데!
본격적으로 일을 시작하려니 참 아쉽게 되었습니다. 

2017년 5월 22일부로 가판대 기능을 닫았습니다.

가판대 기능을 살려보기 위해 앱도 만들고, 푸시서비스도 넣고 쪽지도 만들고 이것저것 했었는데 결말이 좀 안타깝지만, 적법하지 않은 기능이라는 민원 처리 결과입니다.

보건복지부의 고시"보건복지가족부 고시 제2009-24호"에 따르면 게임아이템 거래 중개 사이트는 게임 거래를 알선하는 모든 사이트를 청소년 유해물로 규정하고 있습니다. 옛날 옛적 네이버 카페들이 문닫았던 이유인데요.

청소년들이 게임내 재화를 얻거나 팔기 위해서 많은 시간을 투자해야된다는 개념이 들어가는 이상, 현금 거래이던 아니던 적법하지 않다고 합니다. 위반하면 형사처벌이고요. 본인인증을 도입한다고 하더라도 광고 수입이 그정도도 없고, 접근성도 네이버 카페보다 뛰어날것이라고 생각하지 않습니다.

가판대 기능은 꽤 오래 유지가 되었었는데 (사용량은 아주 없었지만), 여태 신고 안당한걸 천운으로 생각해야될것 같습니다 -_-;

원래 처음 싴갤러스를 시작할때는 법령을 고려해본적이 한번도 없는데, 주변에서 쓰러져가고 고소같은거도 정보를 제공하면서 이제 단순히 의지만으로 현실을 관철할 수 없지 않게 되었나 생각합니다. 많은 아쉬움이 따르지만, 그간 준비했던 기능들은 다른 곳에  쓰일 수 있길 바랍니다. 뭐 이번에 대통령도 바뀌었는데 다시 진행하게 될 수 도 있죠. see you later!
2016/09/26 08:06 2016/09/26 08:06

시작하며


사이트를 만들고 서비스하면 잘 쓰이는 기능도 있는반면, 잘 쓰이지 않는 기능도 존재하는 법입니다.
가판대는 싴갤러스 가장 초기에 만들어진 기능중 하나였으나 사장된(...) 기능입니다.


꽤 손을 많이 탄 기능이고 애착이 있기때문에, 어떻게든 활성화 시킬 수 없을까 하고 여러 고민을 해보게 되었습니다.
(사실, 네이버 카페보다 훨씬 편하다고 생각했기 때문입니다.)

그래서 예전처럼 마구자비로 개발하기보단 생각과 고민을 충분히 해보기로 했습니다.

문제분석과 시도

1. 이름

처음엔 '브라우니' 라는 이름의 메뉴-개인상점 브라우니에서 착안-였으나 사용자들이 어떤 기능인지 잘 모를것 같다는 생각을 반영해, '싴갤러스 가판대'로 이름을 변경했습니다.

2. 편의
처음엔 스크린샷을 넣을 수 없었습니다. 당연히 글로 모든걸 설명해야했습니다. 이는 의도된것으로 스크린샷 한장만 올리는게 아니고, 옵션을 모두 치게 하면 검색이 더 잘될것이라 판단한것입니다만.. 세상에 누가 내구에 색상같은걸 다 치고 있을까요..
그래서 스크린샷을 넣을 수 있도록 개선하였습니다. 또한, 마우스클릭만으로도 쉽게 금액을 추가할 수 있도록 했습니다. 은행앱에서 착안을 해봤습니다

강점 / 약점 분석

당연하지만, 위 두가지를 개선(?) 한다고 해서 당장 효과를 볼 것이라고 생각하지 않았습니다.
롤 모델을 정해 분석해보기로 했고, 당연히 가장 활성화된 마거카를 대상으로 하였습니다.



0. 인증된 사용자(강점)
네이버 카페는 가입절차가 복잡하지 않아 자유롭게 가입할 수 있습니다. 하지만, 해당 캐릭터가 실존하는지, 아이디를 돌려가며 장난을 치는지 알 수 없습니다.
싴갤러스는 특수한 경우(프록시 등)를 제외하면 부계정 사용시 특별조등 기능 사용에 지장이 오기때문에, 인증된 사용자만이 서비스를 이용하고 있다고 할 수 있습니다. 가입의 편의성보단 아무래도 이쪽에 무게가 실릴것 같았습니다.

1. 연락망(약점)

네이버 카페에는 댓글 및 쪽지가 존재하며 타 기기를 이용하는데 제약이 없어 PC에 한정되지 않아 수월하게 대상과 거래를 진행할 수 있습니다.
하지만, 싴갤러스는 인게임 캐릭터에게 쪽지를 보내는 기능이 존재하나 일방적인 기능이어서, 수월한 대화를 진행하기는 어렵습니다.
따라서, 기능에 대한 보완을 하기 위해 신규개발이 필요하다 생각했습니다.

- 사이트 자체 쪽지 기능 개발
- 어플리케이션을 이용한 Push(쪽지 연동)

2. 레이아웃(약점)

네이버 카페는 기본적으로 게시판입니다. 싴갤러스 가판대는 하나의 아이템을 하나의 글로 등록하는 방식을 취할것이기때문에 많은 데이터가 올라올 수 있고, 오히려 가독성을 저하시키는 효과를 가져올 수 있습니다. 아울러 모바일 레이아웃까지 동시에 생각해야했기 때문에 많은 고민이 있었습니다.

3. 기능(강점)

네이버 카페는 위지윅에디터를 이용한 자유로운 글 편집이 가능합니다.

하지만 그외 실제 게임에 도움이 되는 기능은 존재하지 않습니다.
예를 들면, 지정염색앰플을 판매한다 가정하면, 지염 색상을 싴갤러스에서 검색 후, 스크린샷을 찍어 올려 거래하고 있음을 확인했습니다.
계속해서 말하자면, 유저는 결국 '용지염 다수' 라는 글 제목으로 글을 쓸 수밖에 없고, 구매자는 클릭 후 자신이 원하는 색상을 찾은 후에 없으면 성과없이 뒤로 돌아가는 불필요한 과정이 존재합니다.

따라서, 보는 유저나 쓰는 유저 둘다 편하며, 정확한 아이템 이름 및 표기해줄 수 있도록 하는것을 강점으로 하면 될 것 같았습니다.
그래서, 다음 개발이 필요시 되었습니다.

- 유저 오타 교정(데이터 분석을 기반)
- 아이템 이미지 삽입 기능
- 다수의 아이템을 편하고 빠르게 등록
- 아이템별 카테고리화 (카테고리별 검색 및 목록 필터)

강점 및 약점 분석을 다 마친것 같으니 개발하면서 디테일을 설정하기로 했습니다.

개발진행

연락망

레이아웃을 만들때 했던 고민을 또 해야 했습니다.
추가 개발이지만, 결국 독립적으로 사용될 수 있는 기능을 만드는것이기때문에 필요한 사항을 정리해봤습니다.

- 발신함 / 수신함
- 쪽지 발송
- 삭제/신고/차단/보관
- 전달(?)

쪽지란것은 그냥 핑-퐁만 하면 되어서 구현에 어려움을 겪진않았습니다.
다만 악성유저에 대한 처리를 할 수 있도록 고려해야했습니다. 하지만 가판대를 위한 개발이었으므로, 우선사항에서 미뤘습니다.
왜냐면 단순히 차단이나 신고로 끝나는게 아니라, 관리자가 신고를 접수해 처리할 수 있는 기능을 추가로 개발해야되기때문입니다.
그 결과 다음과 같이 개발했습니다.


실제 사용자가 접속중일때 신규 쪽지가 왔다고 알려줘야되므로, 다음과 같이 3단계에 거친 작업이 필요했습니다.
웹서버 - 세션 - 푸시서버
세션을 따로 관리하고 있기때문에 세션을 얻고, 개인푸시를 진행하게 함으로서 웹소켓을 이용한 푸시를 구현하였습니다.
(추가로, 브라우저를 이용한 web push도 지원할 계획입니다. https에서만 가능해서 현재는 진행을 하지 못하고 있지만 ;D)

여기까지만 만들어놓고 즉시 앱 프로젝트를 진행했습니다.

앱 프로젝트는 Sparrow(참새) - 지저귀다 뭐 이런의미로 시작했습니다. (처음엔 그냥 직박구리로 하자고 했다가..)
개발은 iOS부터 했고, 이후에 Android를 개발했습니다. (9월 11일 출시했습니다)

이 과정은 YOKO.SO 프로젝트를 담당하셨던 라제폰님께서 전담해주셨습니다.
(다운로드는 플레이스토어나, 앱스토어에서 '싴갤러스'를 검색해 받으실 수 있습니다)



앱은 정말 쉬울 줄 알았습니다만 그렇게 생각하다가 큰 코다쳤습니다. 간단한 웹뷰앱이었는데도 공수가 많이 들었습니다.
대표적으로, 앱에서 로그인을 매번하게 할 수는 없었습니다. 저 같아도 그럼 안쓸거같아요.

싴갤러스에서는 따로 자동로그인을 지원하지 않았는데 앱을 위해서라도 로그인을 유지하는 기능이 필요하다고 생각하게 되었습니다.
자동로그인 기능은 결국 클라이언트에 정보를 남기는것이기때문에 보안상 문제가 생길 수 밖에 없고, 최대한 '덜 취약한' 형태로 구현하는것이 목적이었습니다.

실제 plaintext를 알고있다고해도, 어떤 방법으로 토큰이 생성되는지,
무작위 대입공격으로 유추할 수 없을정도로 긴 길이의 토큰을 생성해야하는 생각을 하면서 작업했습니다.
그리고 만약의 경우, 토큰을 복호화할 수 있는지 없는지에 대한 방법도 고민해보고.. 최대한 secure하게 작업하고 싶었습니다.
(물론 해쉬기때문에 쓸데없는 고민입니다)

몇차례 시행착오를 거쳐 자동로그인을 도입했습니다.



이 후, 앱을 위한 사이트 전체 CSS 수정이 있었고(대부분 모바일), 여전히 진행중입니다.
또한 푸시를 위한 디자인도 해야했는데 어떻게 하다보니 된것 같습니다 (대책 없는 설명이지만 정말 어쩌다보니 푸시를 순조롭게 넘은것 같습니다. 오픈소스의 위력을 다시한번 느끼기도 했습니다.)

어떻게 뚝딱 해치운 것 같지만, 아직 넘어야할 산이 두개나 남아 있죠.
바로 본기능인 가판대가 남았습니다. 가판대는 아직 시작도 안했기때문에, 작업 후 후기(?)로 찾아뵙겠습니다.



싴갤러스 - 하나의 기능 리뉴얼을 준비하며 (2)로 이어집니다
2016/09/14 19:21 2016/09/14 19:21

시작하며


제발 마지막 글이 되었으면 좋겠지만..

두번에 걸쳐 HSL과 delta E (CIE 2000)에 거친 색 정렬을 시도해보았으나 정렬된 결과가 아무래도 맘에 들지 않았다.
도대체 21세기에! 색을 정렬하는 방법이 없나?라는 의문을 가지고 구글링했으나.
맞다. 색을 정렬하는 방법은 없다.... 이상한 영상이 하나 있긴한데 색상을 기준으로 하는건 아닌것 같다.

이 글을 쓰게 된데는 이유가 있다. 바로 이 글을 봤기 때문이다.

Unfortunately this idea runs into a basic problem: You can’t sort colors. Because the human eye has three distinct color sensors (red, green, and blue), color is fundamentally a three-dimensional quantity, and there is no linear ordering that brings together “similar” colors. If you sort first by amount of red, for example, then you may bring together wildly different hues and brightnesses. If you sort by hue, then you bring together wildly different degrees of saturation and brightness, and so on. There’s just no way to do it.

댓글에 애초에 베이스부터가 틀렸다고 색 정렬 못한다고 하는데 이대로 끝낼 순 없지않은가?
이가 없으면 잇몸으로라도 한다고... 정렬하는 척이라도 해야지 않을까?

그래서

처음에 접근한 방법은 휘도, 채도를 이용해 HSL로 색을 정렬하는 방법이었다.
공식에 사용되는 수치는 조금씩 다를 수 있지만 원리 자체는 별차이가 없다.

이 방법은 SQL 쿼리만으로도 뽑아낼 수 있다. (아주 편함)
order by SQRT(`R` * `R` * 0.241 + `G` * `G` * 0.691 + `B` * `B` * 0.068)


다음과 같은 SET을 얻을 수 있었다. 온전히 색의 밝기만으로 정렬하는것이기때문에
색상들이 진행될수록 어두워지는걸 알 수 있다. 하지만 정렬이라고 보긴 어렵다.



그 다음으로 시도한것이 delta E (CIE 2000) 으로 색상사이의 간격을 계산 후 정렬하는것이었다. (color distance or difference)
사실 여러방법으로 시도하는 글이 있고, 여러 알고리즘이 있지만, 결국 원리는 비슷했기때문에 요방법만 해보기로 했다.

HSL 로 정렬한 SET에서 색을 한개씩 뽑아내 그 색으로부터 가장 가까운 색을 뽑는것이다.
이 방법은 꽤 괜찮은 시도였다고 생각한다.



꽤 군집이 생긴것 같지만, 여전히 '완전한' 정렬이 되었다고 보긴 어려웠다. 갑자기 회색톤으로 가다가 빨간색이 나오질 않나..
그래서 정렬하는 과정을 꼼꼼히 살펴보기로 했다. 예를 들변 핫핑크에서 꽃핑크를 선택하는 과정은 다음과 같았다. 길어서 중간은 좀 잘랐다.

[!]블드핫핑크
[INIT] 레몬(금속) 155.26638858087
[CHANGED] 리옐(금속) 152.03349927037 
[CHANGED] 리옐 151.11097423585 
[CHANGED] 핫핑크 10.281657185973 
[CHANGED] 꽃핑크 7.5335844516123 
[ADDED] 꽃핑크

[!]꽃핑크
[INIT] 레몬(금속) 150.23029944029
[CHANGED] 리옐(금속) 147.51696619098 
[CHANGED] 리옐 146.71284135744 
[CHANGED] 딥핑크 13.359231425896 
[CHANGED] 딥핑크(금속) 13.359231425896 
[ADDED] 딥핑크(금속)


색상거리의 최소값을 계산하는 과정인데 하자가 없어야했다. 이론대로라면 완전히, 깔끔하게 정렬되어야 하는데 왜 저런 결과가 나온걸까?
도대체 중간에서 오른쪽쯤에 떨어진 저 빨간색은 왜 거슬리게 저기 있어야만 한걸까?

저 친구의 이름은 블드체리인데, 저 친구도 물론 후보로 거론된적이 잇는 친구였다. 하지만..

[!]체리분홍(금속)
 [CHANGED] 블드체리 16.814328547159 
[CHANGED] 블드탁한체리분홍 6.9852497800933 
[ADDED] 블드탁한체리분홍

[!]블드탁한체리분홍
[CHANGED] 블드체리 15.356974641448 
[CHANGED] 블드체리분홍 10.401343164254 

[CHANGED] 블드체리 22.072876833766 
[CHANGED] 블드핫핑크 5.735796527615 
[ADDED] 블드핫핑크

[CHANGED] 블드체리 18.265066068665 
[CHANGED] 핫핑크 17.31315523967 
[CHANGED] 딥핑크 13.359231425896 
[CHANGED] 딥핑크(금속) 13.359231425896 
[ADDED] 딥핑크(금속)

[CHANGED] 블드베이비핑크 48.921819262293 
[CHANGED] 블드체리 40.790667505018 
[ADDED] 블드체리


밀리고 밀리고 밀리다가 색상차이가 40이나 되는 상관없는 색에게 선택받을 수 있었던것이다.
코드상으로는 최소한의 색상차이가 나는 색상을 뽑았기때문에 문제가 없지만, 이는 정렬이 되었다고 판단할 수 없게 만드는 가장 큰 요소였다.
근소한 값으로 차이가 발생했다고 하더라도 선택되지 않으면 다음 색상 선택에서 완전히 달라진 결과를 도출하기 때문이다.

그래서 오차를 줘서 후보군을 구제해보기로 했다.
블드체리 색상은 11정도의 색상차이로 탈락하기 때문에 얼마나 큰 차이가 있을지 비교해보기위해 시각화해서 비교해보기로 했다. (인간의 눈 기준으로)
다음은 그 결과이다.



뭘 보라고 하는지 모르겠는 사람도 있을 것 같지만, 3번라인을 보면 튀는 값도 없고 정렬됐다고 할 수 있을지도 모른다. 흠..
그렇다면 최종 선발된 값과 탈락한 후보군들과의 색상차를 모두 다시 계산해 준다면 어떨까?

요컨데 이런식이다.

[!]탁메론
...
[CHANGED] 연두 11.174564290748 
[CHANGED] 블드연두 8.8682969251542 
[ADDED] 블드연두
...
[CHOSEN]블드연두 ->블드연한메론 18.812444073324
[CHOSEN]블드연두 ->연두 3.1295681624566
[ADDED2] 연두


과정을 되새김질해서 아깝게 탈락한 후보군들을 재활용해보겠다는것이다.
위와 같이 연두컬러는 블드연두라는 컬러와 겨우 3차이의 색상차가 발생해 탈락하지만, 재검사로 바로 다음에 색상을 배치하는것.
그 결과는...


여전히 카오스지만 꽤나 스무스해졌다(고 생각한다)
이 중에서 제일 괜찮아 보이는 4번라인으로 최종선택을 했다.

결론





결과적으로만 놓고보면... 거의 차이가 없다 ㅠㅠ.. 그치만 블드체리 친구(빨간줄)이 사라졌다....... 이게 소득일까...?

사람이 판단 하는 색상과 기계가 판단하는 색상을 동일하게 볼 수 없다는것이며, 휴먼소팅(...)을 해야 예쁜 그라데이션을 만들 수 있을 것 같다.

이 삽질의 흔적은 싴갤러스 지염 도서관에서 찾아보실 수 있습니다...





2016/05/22 00:43 2016/05/22 00:43
이 글은 HSV(HSL) for DB query - Sort color by lightness 에서 이어진 글입니다.

HSL은 밝기 기준으로 색을 정렬하는 기준이다.
하지만, 무작위 배열을 선택해 정렬했을 때는 전혀 정렬한것 같게 보이지 않는다는 단점이 존재한다.
복습해보자면, 간단한 쿼리로 HSL을 계산해서 색을 정렬했었고 그 결과는 다음 이미지와 같다.



사실 표본이 작기 때문에 이정도로도 충분히 정렬되었다고 판단할 수 있다.
하지만 모든 표본을 나열해보면 그렇지가 않다.
다소 길지만 비교를 위해서 그냥 올려본다. 양해를 구한다.



모든 표본을 나열했을때 밝음에서 어두움으로 넘어오곤 있지만, 사실 밝기 diff는 몰라도 사람의 시각으로는 이 데이터는 '정렬' 되었다고 말할 수가 없다.
물론, HSL이 쓸모가 없다는것은 아니다. 단지, 무작위 데이터를 처리할때 한가지 더 처리를 해줘야된다는것을 얘기하고 싶은것이다.

color Difference를 구하는 방법은 여러가지가 있으며 위키피디아에서 다수의 방법을 확인할 수도 있으며,
휘도(Luminance)로 정렬하는 방법도 존재한다.

하지만 휘도는 결국 Brightness 로서 HSL과 별 다를바가 없으며, 정렬햇을때도 결과가 비슷했다.
여러 방향으로 접근을 해봤으나 결론은 그라데이션(gradient)를 구현하려면 한가지 처리를 더 해야겠다고 생각했는데 (말은 쉽게하지만 나름 고민 하느라 많은 시간을 보냈다 ㅠㅠ) 그러다가 이 글을 보게 되었다.

내가 얻은 결론은 한가지 색을 뽑고 그 색의 color DIff를 계산해서 근접한 값을 모아줘야된다것이다.

데이터가 200개도 안되는 작은 숫자였기때문에, 선택 정렬(selection sort)을 이용해 정렬을 해볼까 했다.
2단 루프로 o(n^2)로 엄청나게 느리지만 표본이 적고, 먼저 HSL로 정렬된 값을 뽑기때문에 가능할거라 생각했지만..
중복처리를 하려면 답이 안나와서 결국 멋대로 작업해보기로 했다.
루프를 하나 돌고, 어레이를 삭제하고 초기화하는 좀 극단적인 방법이지만 별 다른 수가 떠오르지 않았다.....ㅠㅠ

색 판별은 delta E(CIE 2000) 알고리즘을 사용하기로 했다. color diff를 계산하는 방법은 여러가지가 있었으나, 색사이의 distance를 계산해야했기때문에 이 알고리즘을 선택했다.
라이브러리르는 직접 구현하지 않고, github에서 찾아다 썼다.

$sArray = Array();
$sArray[0] = $array[0];
$count = 0;

$i = 0;
while ( count($array) ) {

    $diff = "";
    $pointer = "";

    for ($j = 0; $j < count($array); $j++ ) {
        $cDiff = (new color_difference())->deltaECIE2000([ $sArray[$i]['colorR'], $sArray[$i]['colorG'], $sArray[$i]['colorB'] ], [ $array[$j]['colorR'], $array[$j]['colorG'], $array[$j]['colorB'] ]);
            if ( $diff == "" ){
                $diff = $cDiff;
                $pointer = $j;
            } else {
            if ( $cDiff < $diff )  {
                $diff = $cDiff;
                $pointer = $j;
            }
        }
    }

    $sArray[] = $array[$pointer];
    if ( $i == 0 ) {
        unset($array[0]);
    }
    unset($array[$pointer]);
    $array = array_filter($array);
    $array = array_values($array);
    $i = count($sArray)-1;

}


그 결과이다.


아래 그림은 최종본은 아니고, 실제로는 정렬되어있다.
재밌는 점은 분명히 이렇게 엮일거같다고 생각한 색들도 알고리즘에 의해 계산된 숫자 수치로는 다르다는점이었다
사람의 시각으로 정렬하는게 맞겠지만.. 일단은, 여기까지!



색상 정렬 (Color sort) 맺으며.. 에서 이어집니다.


Reference
https://en.wikipedia.org/wiki/HSL_and_HSV
https://en.wikipedia.org/wiki/Color_difference
https://github.com/renasboy/php-color-difference
2015/10/20 03:16 2015/10/20 03:16
색을 정렬하는 방법은 무엇인가.

운영하는 사이트 컨텐츠중 지정 염색 앰플 도서관에 들어갈 색상을 정렬해야 하는 문제가 생겼다.

db set은 대강 이렇게 생겼다.

빕분홍 255 170 170 #ffaaaa
실버분홍 164 129 140 #A4818C
그레이체리 142 128 138 #8E808A
탁분홍 197 141 141 #c58d8d
진달래 172 72 126 #ac487e
진한딸기우유(진딸우) 255 111 189 #ff6fbd
딸기우유(딸우) 255 129 211 #ff81d3
딥핑크 160 19 64 #a01340
핫핑크 199 25 103 #c71967


처음에는 대수롭지 않게 hex (web color code)대로 정렬해봤다.
간단히 webcode를 desc대로 정렬해봤다.


정렬은 되는데 뭔가 이상한..?
정렬이 안된다는 느낌이라고 해야하나..
다른 결과를 좀 내려다보니 아니나 다를까, 정렬이 되고 있지 않았다.
아니 정확히 말하자면 이렇게 정렬이 되어선 안됐다



딱봐도 이상하잖아..

그래서 이것도 그냥 정렬해서 안되겠구나 하고 찾아보니 색은 HSV나 HSL 이라는 하이라이트(밝기) 기준으로 정렬해야된단다.
관련 문서 같은건 위키나 이곳의 샘플코드를 보면 되겠다.

이렇게 한창 만들고 있다가, 잘 생각해보니 정렬을 위해선 모든 dataset에 배열안에 다들어가 있어야되는데,
read해서 바로 뿌려주는 내 페이지에는 비적합하다는 생각이 들었다. dbset이 몇개가 될줄알고 그걸 정렬을 위해 다 들고 있어야되나 이 생각이 들었다. (물론 이건 케이스마다 다른얘기)

그래서, 애초에 쿼리로 HSL을 적용해서 불러오면 어떨까 생각이 들었다.

결론부터 말하자면 웹컬러코드는 필요 없다. RGB색상이 필요하다.

select * from TABLE order by SQRT(colorR * colorR * 0.241 + colorG * colorG * 0.691 + colorB * colorB * 0.068) desc


HSV나 HSL은 결국 채도, 밝기에 의해 정렬되는것이기때문에 색상별로 가중치를 줘서 더한값을 정렬해야한다.
그 값은 쿼리에 나와 있듯이 R*R*0.241 + G*G*0.691 + B*B*0.068 값으로 구성된다.
즉, 그라데이션 같은 값으로 나오진 않는다. 하지만 색상정렬이 밝기별로 되고 있다는 느낌은 확실히 받을 수 있다.

다음은 위 쿼리를 설정해서 검색해본 결과.



1) Sort color using delta E(CIE 2000) - Similar Color gradient 에서 이어집니다
2) 색상 정렬 (Color sort) 맺으며.. 에서 이어집니다

2015/06/23 15:15 2015/06/23 15:15