2018년 12월 26일 수요일

여기에 압축 풀기 기능에서 한글 파일 깨짐

zip 압축파일에 한글 파일명이 포함되어 있을 때 zip -F 명령을 거치는 방법은 예전에 봤었고, unzip -O cp949 식으로 옵션을 줄 수 있는 건 새로 알았다.

그놈 기본 파일 관리자인 노틸러스를 그냥 쓰고 있는데, 압축파일을 우클릭하면 나오는 "여기에 압축 풀기" 항목으로는 여전히 한글이 깨진 채로 압축이 풀린다. 이 기능을 쓰면 얼마 전까지도 file-roller 창이 그냥 떴던 것 같은데 언젠가부터 노틸러스 창 안쪽에 동그라미로 진행률이 표시되다가 끝나는 방식으로 바뀌었다. 그래서 아마 nautilus 자체 기능으로 뭔가 들어갔겠거니 싶어서 찾아보았다.

apt source nautilus 명령으로소스를 받아온 다음 grep Extract Here -ri로 전체를 찾아보았다. nautilus-files-view.c 파일에 이것저것 있다. extract_here로도 찾아보니 action_extract_here, extract_files 같은 그럴듯한 함수들이 나온다. 타고 들어가보면 nautilus_file_operations_extract_files 함수가 노틸러스에 진행률이 표시되도록 연결하는 부분이고 마지막에 언급되는 extract_task_thread_func라는 함수가 실제로 압축 해제를 진행한다.

압축 해제 기능은 gnome-autoar 패키지에 의존한다. libgnome-autoar-0-0 패키지가 있고, apt remove 시도해보니 nautilus 패키지도 같이 제거될 거라고 나온다. https://github.com/GNOME/gnome-autoar/tree/master/gnome-autoar 여기에 소스가 있다.

autoar_extractor_do_sanitize_pathname 함수를 거쳐 autoar_common_get_utf8_pathname 함수를 보면 UTF-8 파일명이 아닌 경우에 3가지 인코딩을 하드코딩해두고 차례로 변환을 시도한다. 이 과정에서 EUC-KR 한글 파일명이 깨진 문자열로 바뀌는 걸 텐데, 현재 소스코드에서는 환경변수를 참조하는 등의 열린 구조가 아니어서 바로 해결은 안 되겠다. G_FILENAME_ENCODING 같은 게 아직 쓰이나 모르겠네.

2018년 12월 23일 일요일

사용자가 업로드한 파일을 즉시 CDN 주소로 제공하는 방법 구상

사용자가 방금 업로드한 파일을 즉시 사용자에게 다시 보여줘야 하는 경우에 CDN을 어떤 구성으로 적용할지 궁리를 해왔다. 사용자에게 항상 CDN으로 파일을 제공한다는 전제로 구성하다 보면 사용자가 자체 서버로 업로드한 파일을 CDN으로 다시 배포하는 시간만큼 사용자에게 제공하는 시간이 지연된다는 부분에서 늘 막혔다. 사용자-자체서버-외부저장소-CDN 순서로 전송이 이루어지다보면 파일의 크기에 비례해 2~3배 만큼의 지연이 발생하는 구조가 되는 거였다.


그래서 이런 시나리오를 생각해봤다.
  1. 사용자는 자체 서버로 파일을 업로드를 한다. (트래픽 1차)
  2. 자체 서버에서는 직접 파일을 서빙할 수 있는 모든 준비를 마친다.
  3. 자체 서버는 미리 설정된 외부 저장소에 0바이트 크기로 blob을 만들고, 해당 blob에 custom header를 설정해서 Location 헤더가 앞서 준비한 자체 파일 서빙 URL을 가리키도록 한다.
  4. 자체 서버는 미리 설정된 CDN URL을 사용자에게 제공한다.
  5. 서버가 제공한 CDN URL에 사용자가 접속한다.
  6. CDN은 Location 헤더로 자체 서버의 URL로 다시 이동한다.
  7. 사용자는 CDN URL에서 redirection된 자체 서버의 URL로 실제 파일을 제공받는다.
  8. 그 사이 서버는 외부 저장소에 앞서 생성한 blob의 본문과 custom header를 실제 파일 내용으로 교체한다. (트래픽 2차)
  9. blob에 교체되면 CDN에서는 적당한 시점에 재배포된다. (트래픽 3차)
  10. 외부 저장소에 CDN 배포에 반영된 후에는 사용자의 접속으로 CDN이 직접 파일을 제공한다.
  11. 자체 서버는 백업용 혹은 CDN으로 제공할 수 없는 경우에 사용한다.

AWS로 중요 부분을 구현해보면 이렇다. 수동으로 했지만 아마 API로도 될 것이다.
  1. S3 버킷을 만들고 static website 기능을 켠다.
  2. 빈 파일을 하나 만들어서 blob으로 올리고 Website-Redirect-Location 헤더를 적당한 다른 URL로 지정한다.
  3. 해당 S3 blob에 static website URL로 접속했을 때 Location 헤더가 붙어오는 걸 확인한다. 그냥 blob 자체의 URL로 접속하면 x-amz-website-redirect-location 헤더로 나온다.
  4. CloudFront 배포를 하나 만들고 필요한 설정을 한다.
  5. CF의 Origin은 설정화면에서 제공하는 S3 항목을 그대로 선택하면 안되고, S3 static website 활성화 후에 생성된 도메인을 적어준다. (https://stackoverflow.com/a/41132075/6629746 참조)
  6. 해당 blob을 CF 배포의 URL을 통해서 접속했을 때 location 헤더가 붙어오는 걸 확인한다.

GCP나 Azure는 확인하지 않았지만 대체로 비슷한 수준의 지원을 할 것으로 예상한다.

아예 파일을 외부 저장소로 직접 업로드하는 방법도 있다.
FORM 내의 INPUT 항목에 (보통이라면 request header로 포함했을) 인증값과 저장할 blob 이름, custom header 등을 지정해서 S3에 직접 POST를 날리면 된다고 한다. 이쪽이 당연히 간편하긴 하겠는데, 제공하는 파일이 전체공개가 아니어서 외부 CDN으로 배포하지 않아야 하는 경우를 처리할 수가 없어서 깊게 검토하지 않았었다. 전체공개 파일을 아예 분리해서 다루는 방식이라면 채택할만하겠다.

2018년 12월 21일 금요일

나이스페이먼츠 모바일 연동방식 개선 - 아임포트 공지 기준으로

2018-10-17 날짜로 "[공지] 나이스페이먼츠 모바일 연동방식 변경 안내"라는 제목의 메일이 아임포트에서 왔었다. 아임포트를 검토만 했을 뿐 직접 쓰고 있지는 않았기 때문에 미뤄뒀다가 나중에 내용을 자세히 읽어봤는데 이미 2016년경부터 적용한 방식이었다. 메일 자체는 온라인에 기록된 건 없는 것 같고 github에 해당 안내가 있다. "niceMobileV2"라고 찾아보면 나온다.

의외로 경력자 중에 결제 연동 경험이 없는 경우도 많았던 것 같고, 나도 개념을 까먹기 전에 기록해두기 위해, 아임포트의 공지가 어떤 내용이고 어떤 문제를 해결하는 것인지 짤막하게나마 정리를 해보려고 한다. 아래 내용은 나이스페이 기준으로 설명하는 것이지만 모바일 환경이기 때문에 발생하는 문제라서 꼭 어느 PG사에 국한되지는 않을 것이라 생각한다. (대충 기억하기로는 이니페이도 비슷한 방식이었다)

나이스페이 결제는 기본적으로 인증요청-인증결과응답-승인요청-승인응답 순서로 진행된다. 카드번호를 넣거나 카드사별 간편결제 절차를 진행하는 것이 인증요청에 해당하고, 인증 정보를 가지고 다시 서버가 PG사와 통신해서 실제로 결제를 일으키는 것이 승인요청이다. 승인까지 진행되면 '결제가 되었다'고 할 수 있고 카드사에서 문자나 푸시로 결제가 발생했음을 알려준다. (이 시점에 받은 승인응답을 DB 같은 자체 기록으로 남겨두면 된다)

PC 결제에서는 iframe 혹은 다른 방식으로 웹페이지 안에 다른 화면을 표시하는 것이 화면 구성상 크게 무리가 없기 때문에 위의 인증 및 승인 절차를 서비스의 URL을 벗어나지 않고도 진행할 수 있다.

하지만 모바일 결제는 얘기가 다르다. 나이스페이는 인증요청 시작 단계부터 서비스를 벗어나서 나이스페이 URL로 화면이 완전히 바뀌어야 하고, 그 안에서 카드사 간편결제 앱이라도 호출하면 현재의 브라우저 혹은 앱 조차도 벗어나게 된다. 따라서 결제 정보가 어디에서 어디로 가는지, 그때마다 화면은 어떻게 나오는지 신경을 써야 한다. 다시 돌아오는 것조차도 난관이다.

모바일 환경에서도 인증 및 승인 절차는 당연히 필요하고 사용자가 보면서 인증을 진행하는 건 바뀔 구석이 없다. 승인 절차가 어떻게 되는지가 문제다. 나이스페이의 모바일 결제에서 승인은 두 갈래로 나뉜다.
  1. 하나는 승인요청을 나이스페이가 미리 처리한 다음 승인응답 결과만을 서버로 직접 건내주는 방식이다. 그 뒤에 나이스페이는 사용자를 처음 인증요청할 때 지정했던 URL로 돌아가라고 보낸다. 돌아온 URL에서는 (아까 서버가 받은 승인응답 결과에 따라 처리가 끝난) 완료  화면을 보여주게 된다.
  2. 다른 하나는 PC와 동일하게 사용자가 돌아온 시점에서야 승인요청을 진행하는 것이다. 아임포트가 공지한 niceMobileV2: true 옵션은 이 방식을 따르도록 만든다.

첫번째 방식대로 진행이 되었을 때 생기는 문제는 이렇다. 모바일 결제에서 사용자가 인증을 마친 뒤에 화면이 완전히 바뀔 때까지 서버는 반드시 승인요청과 승인응답 및 그 기록 절차를 마쳐야 한다. 그런데 여러가지 이유로 가끔 이 전제가 지켜지지 못할 때가 있다. 이러면 이미 인증을 마쳤고 나이스페이가 카드사와의 승인 절차도 끝내서 카드결제 문자는 받았으니 사용자는 다 잘 된 걸로 아는데, 정작 서비스는 결제가 안된 상태로 남아있게 된다. 그렇게 누락된 결제건이 있다는 사실 자체도 모를 수 있다. 항의 전화가 들어올 차례다.

두번째 방식은 첫번째 방식에서 생기는 누락을 완전히 없앤다. 사용자가 다시 돌아오지 못해서 결제가 승인되지 못하면 그냥 그뿐이다. 서비스의 전환률이 낮아질지언정 (이건 고치면 된다) 고객 불만과 불필요한 환불 업무는 생기지 않는다. 덤으로 PC 결제에서 쓰는 코드를 그대로 가져다 쓸 수 있다는 장점도 있다. 해피 엔딩.

2018년 12월 20일 목요일

본 대출자는 00세 남성으로 공공기관에 재직 중입니다-로 시작하는 투자 상품 설명을 읽으며

나는 소위 재테크라고 하는 분야에 밝지 못하다. 투자 성향 진단을 하면 보수적인 타입으로 나온다. 손에 쥔 것도 없고 손에서 놓아내는 걸 겁내기도 하고. 그래서 경험치를 높이고자 은행에서 대출도 소액으로 해보았고, 약간의 여유자금이 생기고부터는 예적금도 만들고, CMA도 이것저것 만들고 P2P 투자도 해보았다. 증권도 소액으로 몇 번 사고 팔고 하면서 주식 시세라는 것이 어떻게 흘러가는지 대강은 체감하고 있다.

P2P 투자도 업체가 많고 아직은 원숙기라고 할만한 시장은 아니니 몇 군데 업체에 소액으로 투자를 했었다. 렌딧이 이모저모로 귀에 많이 들리던 업체라 가장 먼저 찾았고, 부동산 투자를 주로 한다는 업체도 기웃거리다가 그 중에 하나를 골라서 두 번 투자금을 넣어보았다. (토스가 한 때 스스로 대부업체 이름을 달려고 했다가 황급히 발을 뺀 뒤에 서비스에 연결시킨) 8퍼센트도 써보았다.

8퍼센트는 내 기준에는 웹사이트나 웹서비스 구성이 탐탁치 않아서 (초기 가입 때 주는 포인트가 써먹기에 유명무실한 느낌이라 별로라고 판단했던 걸로 기억한다. 웹사이트가 좀 가벼운 느낌이고 그래프 곡률 표시가 정확하지 않은 것도 한 이유였다) 딱 한 번 10만원의 투자를 했다. 1만원씩 10개의 채권을 사들였다.

상품명 "0000호 개인자금", 이것이 현재 8퍼센트에서 사들였던 10개 채권 중 유일하게 남은 채권이다. 8퍼센트가 표기한 등급은 A-, 10개 중 두번째로 높고, 12개월동안 원리금균등 25%, 월이자지급 75% 방식으로 상환할 계획이었다. 2회의 연체를 포함한 10회의 지급, 2500원을 끝으로 이 대출자는 개인회생을 신청하였고, 3주 정도 뒤에는 "개인회생금지명령"이라는 분류의 내역이 추가되었다. 그로부터 시일이 꽤 지났음에도 8퍼센트가 별다른 변제계획안을 제시하지 않고 있으니 아마도 이 채권의 남은 금액은 물 건너간 것일 게다.

그래서 나는 이 "투자 상품"을 "자세히 보기"하였다. 이 대출자는 30대 남성으로, 12년 가까이 공기업 혹은 공무원에 속하는 직장에 정규직으로 일했다. 월 평균 소득도 나보다 훨씬 높았다. 한 달에 카드를 쓰는 금액은 나와 비슷했다. (카드는 신용정보사에 집계되지 않는 경우도 있다고 알고 있으니 완전히 신뢰할 수 있는 정보는 아니다) 그리고 빚이 9천만원 있었다. 8퍼센트가 표시하는 부채 분류 중 "은행/보험/학자금"으로 잡혀 있으니 나이나 규모를 생각하면 학자금은 아닐 것이고 대충 주택 관련 1금융권 대출이 아니었을까 싶다. 신용등급은 KCB 기준 560점 정도로 낮게 유지되다가 심사 4개월 전부터 아래로 출렁거리더니 어쩐 일인지 심사 시점에는 급하게 올라 832점을 기록했다. 이 대출자는 8퍼센트를 통해 4천만원을 빌렸다. 그 뒤는 나도 안다. 비교를 위해 10개 채권 중 첫번째였던 A 등급의 다른 상품도 같이 보았다. 다른 대출자는 30대 여성으로, 월 소득은 더 많고 더 오래 재직했다. 은행 빚도 비슷하게 9천6백만원 있었다. 신용등급은 앞서와 달리 꾸준히 높았다. 5천만원의 대출금은 특이사항 없이 모두 상환되었다.

12년째 공기업에 일하던 30대 남성은 왜 나보다 많이 벌고 카드 소비도 많지 않고 주거도 안정적이었을 것 같은데 굳이 금융권도 아닌 P2P 대부업체까지 와서 대출을 일으키고 변제를 마치지 못한 채 개인회생 제도를 찾게 되었을까? 나이가 많은 건 아니니 본인이 아프거나 한 것은 아니었을 테고, 본인이 사고를 당했다면 대출을 일으키는 것부터가 힘들었을 테니, 어쩌면 부모나 다른 가족이 급환이었던 걸까? P2P 업체에 오기 전까지 금융권은 이미 돌만큼 돌았을 테고 (그래서 몇 달간 신용점수가 떨어졌을 것이다) 실제 부채는 더 많았겠지. 애초에 잡혀있던 9천만원의 부채도 추심이 들어갔을 것이다.

주변에 P2P 투자를 언급할 때 쉽게 표현하기 위해 "쩐주에게 돈을 댄다"고 해왔다. 표현이 좀 속되지만 의미가 다르지는 않으리라 생각한다. 실제로 연체가 발생했다거나 하는 통지가 오면 '내 돈 내놔' 싶은 심정이 되기도 했다. 하지만 마음이 편하지는 않다. 지금도 P2P 업체의 "투자상품" 목록에는 "개인자금"이니 "대환대출"이니 "사업자금"이니 하는 구분으로 여러 줄이 나열된다. 상품이란 이름이 붙어있지만 그 한 줄마다의 너머에는 사람이 있고, 그 사람들은 모두 각자 열심히 살다가 힘든 일이 있어 여기까지 온 것이다. 자본주의 사회에서 힘든 일이란 결국 돈의 문제 아니겠는가.

나는 아직은 다행스럽게도 큰 돈이 녹아날만한 일이 없어서, 마음 편하게 기껏해야 은행 이율보다 높은 이율을 받을 데가 어디 없나 하는 정도로 투자라는 걸 하고 있다. 하지만 언제 어떻게 될지 모르는 일이다. 나보다 더 좋은 직장에 더 오래 일하면서 수입도 더 많던 사람도 빚쟁이가 되어서 낯모르는 사람에게 추측의 대상이 되고 있으니.