어제 고객지원용 프로그램 소스를 보다가 개발팀 일부가 Ping의 역할을 잘못 이해하고 있어서 글을 적게 되었습니다.

발단은 고객 컴퓨터에서 우리가 서비스하는 API로의 연결이 안되는 경우가 있는 것 같은데, 그 경우를 테스트해서 로그를 보내주는 프로그램에서 네트워크 연결 테스트를 ping으로 하더라구요. 결론부터 말하면 Ping은 TCP 연결과는 무관합니다.

 

ICMP구현체, Ping

Ping은 ICMP 프로토콜을 구현한 프로그램입니다. ICMP는 RFC 792에 정의되어있죠. 해당 문서 Introduction 첫파트 내용만 따와서 읽어봅시다.

The Internet Protocol (IP)  is used for host-to-host datagram service in a system of interconnected networks called the Catenet. The network connecting devices are called Gateways. These gateways communicate between themselves for control purposes via a Gateway to Gateway Protocol (GGP). Occasionally a gateway or destination host will communicate with a source host, for example, to report an error in datagram processing. For such purposes this protocol, the Internet Control Message Protocol (ICMP), is used. ICMP, uses the basic support of IP as if it were a higher level protocol, however, ICMP is actually an integral part of IP, and must be implemented by every IP module.

IP(인터넷 프로토콜)은 Catenet이라는 상호 연결된 네트워크의 시스템에서 호스트-호스트 데이터그램 서비스용으로 사용되었다. 기기들을 네트워크로 연결하는 것을 게이트웨이라 부른다. 이 게이트웨이는 GCP(게이트웨이 프로토콜)로 통신을 제어한다. 종종 게이트웨이나 연결대상 호스트는 연결하려는 소스 호스트와 통신을 해야할 경우가 있는데, 예컨데 데이터그램 처리에 발생하는 오류를 알려주어야 한다. 이런 용도로 사용되는 규약이 ICMP이다. IP 프로토콜의 기본 기능을 이용하기 때문에 IP 프로토콜 상위 계층이기는 하지만, 실제로 IP의 일부로 통합되며, 모든 IP 모듈에서 구현되어야 한다.

위 설명에서 알 수 있듯이 ICMP는 실제로는 OSI 7 계층중 Network 계층에 속합니다. 네트워크 연결이라 부르는 것은 전송계층(Transport)계층을 이야기하는 것으로, ICMP는 TCP/UDP의 정보를 가지고 있지 않습니다. 즉 TCP/UDP가 가지는 source port 및 destination port 정보를 접근할 수 없습니다. IP의 연결성 관련 오류를 알려주기 위한 메커니즘으로 이해하는 것이 좋겠죠.

 

IPv4 헤더: 출처:위키피디아
ICMP 헤더 -출처:위키피디아
TCP 헤더: 출처 : 위키피디아

일반 사용자들 중에서는 네트워크 연결 여부를 ping(icmp)로 알아보는 경우도 있는데, 반쯤 맞는 말입니다. 목적지 IP 주소까지 연결이 되면 ping back이 될텐데, 대체로 호스트에서 echo를 보내주면 통신이 되겠죠. 하지만  ICMP 패킷 조차도 네트워크 장비들의 리소스를 사용하기 때문에 그 패킷이 내부망에 흘러들어오지 않도록 네트워크 관리자가 막아버리는 경우도 있습니다. (예: ping flooding)

 

Ping은 막혔지만, HTTP연결은 유효할 수도 있다.

예를 들어 봅시다. 아래 스크린샷은 naver와 cisco에 ICMP를 보내본 것입니다. 네이버 접속은 잘되지만, ICMP는 아예 막아버린 것을 알 수 있습니다. 하지만 네트워크 회사인 시스코는 열어두고 있지요.

따라서 네트워크 연결을 확인하고 싶다면 TCP/UDP 연결까지는 해봐야 합니다. HTTP REST API를 제공하고 있는 곳의 경우, 고객컴퓨터로부터 해당 API가 아무 문제없이 접근할 수 있는지 알아보려면  TCP연결프로그램인 telnet을 이용해서 확인해 봐야죠. 아쉽게도 telnet은 기본 프로그램에서 빠져있기에 별도 설치해야 합니다. 제어판> 프로그램 설치 > 윈도우 피쳐 켜기로 가서 Telnet Client를 설치해 줍시다.

 

 

telnet 설치후 아래 명령을 실행해 보면 연결이 맺어지는 것을 확인할 수 있습니다.

 

자, 이제 정리해 볼까요?

  • network connection을 확인하려면 ping으로는 알 수 없다.
  • ICMP는 네트워크 계층(+살짝 위)에 속하기 때문에 전송계층 TCP/UDP의 포트(port)라는 개념이 없다.
  • ICMP 실패는 네트워크 관리자가 막았기 때문일 수도 있다.

이제 고객지원 프로그램에서 통신 연결하는 코드를 수정하러 갑시다. :)

 

적고보니, 이 글도 토요일에 적고 있네요...

배틀넷 런처.. 새로운 UI로 바뀌었네요.

오랜만에 업데이트했는데, 초기화면에서 바로 L10N 이슈를 찾았습니다.

 

# 맥락없이 번역했을 때 발생할 수 있는 일들

메인화면에서 게임을 즐겨찾기하거나 즐겨찾기에서 게임을 뺄 수 있는데요.

아래 두 화면에서 툴팁을 보시면 바로 L10N(Localization)중 번역 문자열 이슈가 보입니다.

 

 

정황상 소셜 패널에 영문으로 "Add To Favorites"라는 문자열이 두군데 모두 사용되었고, 번역대상 문자열을 뽑다가 어떤 맥락에서 사용된 것인지 모르는 채로 번역팀에 "Add To Favorites"가 전달되어 두 곳 모두  "친한 친구에 추가"로 모두 처리한 것이 아닌가 싶습니다.

이 문제가 번역 과정만의 문제인가? 사실 그렇지는 않습니다.

번역팀(혹은 외부 전문 업체)에서는 소스코드에 접근하는 것이 아니므로 어떤 맥락(상황)에서 해당 문장이 사용되었는지의 정보가 함께 있을 때 정확한 번역이 이루어질 수 있습니다. 

 

예를 들어 크로스 플랫폼 UI를 제공하는 Qt Framework에서 L10N을 지원하기 위해 코드에서는 tr함수를 이용합니다.

QPushButton hello(QPushButton::tr("Hello world!"));

lupdate명령을 통해 소스에서 tr로 된 문자열을 뽑아내죠. 이 과정은 C계열 오픈소스 프로젝트에서 사용하는  po에서도 비슷합니다.

 

보통은 위와 같이 코드에서 다국어 지원을 표시하는데, 번역대상인 문장만으로는 오해의 가능성이 존재합니다.

 

송수신자의 이름을 입력받는 대화창을 생각해 봅시다.

Qt를 이용한다면 다음과 같은 코드가 작성되겠죠.

    QLabel *senderLabel = new QLabel(tr("Name:"));
    QLabel *recipientLabel = new QLabel(tr("Name:"));

lupdate를 하면 dialog.cpp에서 사용되는 L10N대상 문자열은 "Name:"이 추출될 것입니다. 번역팀은 이 정보만 있으니 "이름:"으로 번역해서 업데이트가 되겠죠. 이 번역파일이 적용되면 송수신자 대화상자에 "이름:"이라고만 표시되는 레이블이 2개가 있을 겁니다.

 

이러한 모호성을 없애기 위하여 두번째 인자로 맥락 정보를 제공해 주는 것이 좋습니다.

    QLabel *senderLabel = new QLabel(tr("Name:", "sender"));
    QLabel *recipientLabel = new QLabel(tr("Name:", "recipient"));

즉, "Name:" 이라고 사용되었지만 recipient라는 맥락이 있으니 '이름:'이라는 일반적인 번역 대신 첫번째는 '발송인', 두번째는 '수취인'으로 번역될 수 있겠죠. 정보는 더 많아지지만, 코드는 조금  지저분한 느낌이 드는 것은 사실입니다.

 

비슷한 사례로 인쇄 옵션이 생각나네요. 영어로 가로 출력과 세로 출력이 'Landscape'와 'Portrait'인데, 단어 자체의 뜻은 각각 '조경'과 '초상화'라는 뜻이죠. 그래서 단어만 보고 번역되었을 경우 인쇄 옵션에 생뚱맞게 조경과 초상화가 등장하기도 합니다.

 

개발자분들에게 팁을 드리자면 L10N처리는 문장 단위가 되어야 합니다.

제가 배틀넷 런처 개발당시 겪었던 일을 대략적으로만 소개하겠습니다.(시간은 좀 지났으나..그래도 자세하게 했던 일을 공유하는 것을 피하는것이 제 원칙이라..)

사용자에게 어떤 메시지를 날짜/시간 정보와 함께 표시해야 하는 기능이었는데, 전 포맷팅 문자열을 국가별로 다른 부분만 tr처리를 해서 문장의 일부 부분을 바꾸었습니다. 그랬더니 보스가 절 부르더니 (예가 정확히 기억나지 않는데..) 국가별로 문장 방식이 다를 수 있으니 바뀔 수 있는 부분 말고 문장 단위로 바꾸게끔 것을 가이드하더군요. 그때부터 L10N은 문장 단위로 끊어서 코드에 작성했습니다.

 

 

#L10N, 문장 번역을 넘어서

L10N은 단순히 번역 문제만 말하는 것은 아니고 복잡한 주제들이 있습니다. 대표적으로 표시 형식과 특정 언어만 가지는 문제들이 있죠.

우리가 다루는 데이터에는 날짜, 시간, 숫자(특히 화폐..) 등이 있죠. 날짜만 봐도 2021/03/06, 03/06/2021, 06/03/2021, Mar/06/2021, 2021-03-06 등등 많은 표시형식들이 각 나라별로 있습니다. 화폐단위만 봐도 달러는 센트단위가 있어서 123.45 달러라 표시하면 123달러 45센트가 됩니다만, 원화에서는 소숫점 아래 자리가 없죠. (전이라는 단위가 있긴 합니다만 사용하지 않습니다.)

 

수치표현도 복잡해 집니다. 우리나라를 비롯한 아시아권에서는 주로 만단위의 숫자를 표시하지만 영미권은 천단위로 숫자를 읽습니다. '123,456'의 의미는 뭘까요? 대부분 우리나라사람들은 '12만 3천4백5십6'으로 읽을 겁니다. 하지만 프랑스에서는 '1백2십3 쩜 사오육(소숫점)'으로 이해됩니다. 와우~. 궁금하신분은 Decimal Separator를 읽어보세요. 

 

복수형표시문제, 단어의 성별...

표시형식 문제에 복수형도 복잡하죠.  Qt 문서사이트에 소개된 예를 들어봅시다.

우리나라만 대상으로 하는 소프트웨어를 개발한다고 하면 다음과 같이 코드를 작성하겠죠.

  QString *receivedMessageNotificationFormatter = tr("%d개의 메시지가 도착하였습니다.");

우리나라에서는 몇개의 메시지가 도착하든 상관없이 저 문장 하나로 처리됩니다.

그런데 다음 표를 보세요.

 

프랑스에서는 0개 또는 1개일때는 s가 붙지 않고, 2개 이상에는 s가 붙습니다. (메시지에도 s가 붙고 저장되었다는 동사에도 s가 붙습니다.)

영어에서는 0개또는 2개 이상일 때 s가 붙습니다.(메시지에만 s가 붙고, 동사에는 s가 붙지 않습니다.) 1개일 경우 s가 붙지 않습니다. 

즉, 복수형의 기준 잣대가 언어별로 다르다는 이야기입니다. 특정 국가에서는 단어의 성별에 따라 이어지는 단어의 형태가 바뀌어야 하는 경우도 있습니다.

 

라틴어계열만 그런 것은 아닙니다. 우리말에도 '은는이가'가 있죠.

'닭은 동물이다'와 '고양이는 동물이다'라는 문장이 있죠. '%s은(는) 동물이다'로 표현하는데, 좀 더 매끄러운 문장을 만들려면 %s에 해당하는 단어의 끝 받침에 따라서 '은' 또는 '는'이 선택되어야 하죠.

 

코드에서 L10N을 고려하기 시작하면 챙겨야 할 것들이 많이 있습니다. 하지만 너무 걱정하지 않으셔도 됩니다. 많이 사용되는 L10N 이슈들은 대부분 프레임워크나 운영체제 레벨에서 제공하는 API 또는 정보를 활용하면 됩니다.

 

 

배틀넷 번역때문에 L10N을 문자열 중심으로만 소개했는데, 이미지도 L10N 대상입니다.

우리가 흔히 접하는 이미지(기호)가 국가에 따라 다른 의미를 가질 수도 있기 때문입니다. 널리 알려진 예가 미키마우스 손가락 갯수입니다. 자세히 보신 분들이 있으실 것 같은데 미키마우스는 손가락이 4개입니다. 미키마우스가 일본에서 처음 방영되었을 때 일본 시청자들은 심리적으로 이상한 감정을 느꼈다고 합니다. 지금도 그런지 확인해봐야 합니다만,  일본에서 4손가락은 야쿠자(조폭)이미지를 떠올린다고 합니다. 일본 영화에 가끔 단지 장면이 나오잖아요. "귀여운 미키가 야쿠자라니"라는 무의식이 동작했었다고 하네요. 그래서 L10N에는 번역만 있는 것이 아니라 특정 국가에서 다른 의미나 뉘앙스를 가지는 기호체계를 현지화하는 것이 포함됩니다.

개발에서는 이 문제를 쉽게 resource overriding으로 해결할 수 있습니다.

예를 들어 symbol1.png 이미지가 특정 국가에서는 다르게 바뀌어야 한다면 리소스를 처리하는 부분에서 다음과 같은 방식으로 변경하면 되겠죠? 코드를 아래와 같이 사용하려면 소프트웨어 리소스의 위치 구조도 처음부터 잘 고려해야 합니다.

if (file_exist("{current_locale}/symbol1.png")) {
 return {current_locale}/symbol1.png;
} else {
  return general/symbol1.png;
}

 

배틀넷 런처에서 찾은 번역 오류 이야기하다가 코드 이야기로 빠져서 L10N까지 슬쩍 이야기해보았습니다.

글로벌용 소프트웨어/서비스를 만드는데 작은 도움이 되길 바랍니다.

 

사족

설치/업데이트가 끝나면 "완료하는 중"으로 표시되는데, "마무리 중"이 더 우리글 스럽지 않나 생각되네요.

 

+ Recent posts