도커(Docker) 컨테이너 로케일 설정
데비안(Debian), 우분투(Ubuntu) 이미지에서 한글 입력 문제

들어가며: 컨테이너와 리눅스 로케일 설정

도커Docker를 사용할 때 다른 이미지를 구축하기 위한 베이스 이미지로 우분투나 데비안 이미지가 빈번히 선택 됩니다. 도커에서 제공하는 공식 이미지의 경우 일반적인 리눅스 배포판과 달리 프로세스를 실행하기 위한 최소한의 파일들만이 준비되어 있습니다. 따라서 우분투Ubuntu의 경우 80Mb, 데비안Debian의 경우 120Mb 정도밖에 되지 않습니다. 기본적으로 설정 가능한 로케일 또한 C(POSIX), C.UTF-8 정도로 제한적이며 기본값은 POSIX로 지정되어 있습니다. 이 베이스 이미지들을 기반으로 셸을 실행하는 경우 한글이나 다른 CJK 언어들이 입력되지 않는 문제가 있습니다. 엄밀하게 말하면 한글 입력이 되지 않는 것은 도커 문제는 아닙니다. 하지만 일반 사용자 환경과 달리 컨테이너를 사용하는 경우 로케일 설정으로 인한 문제를 자주 겪게 됩니다.

이 글에서는 로케일에 대해서 간략하게 알아보고, 우분투와 데비안 이미지에서 한글 입력이 가능한 로케일로 설정하는 방법에 대해서 알아보겠습니다.

44BITS 소식과 클라우드 뉴스를 전해드립니다. 지금 5,000명 이상의 구독자와 함께 하고 있습니다 📮

로케일 이해하기: 언어, 지역, 코드셋

로케일은 좁은 의미에서 이해하자면 사용자가 사용하는 언어로 정의할 수 있지만, 실제로는 조금 차이가 있습니다. 일반적으로 language_territory.codeset 형식으로 표현 됩니다. 언어(language)는 ISO 639-1, 지역(territory)은 ISO 3166-1을 따르며, 코드셋(codeset)에는 UTF-8나 EUC-KR과 같은 값이 올 수 있습니다. 이 값들의 조합으로 하나의 로케일이 정의됩니다.

ko_KR.UTF-8 로케일의 의미
ko_KR.UTF-8 로케일의 의미

예를 들어 ko_KR.UTF-8에서 ko는 language, KR은 territory, UTF-8은 codeset을 의미합니다. 즉, 한국어, 한국, UTF-8 조합으로 정의된 로케일이라는 것을 유추해낼 수 있습니다. 과거에는 codeset 값에 EUC-KR과 같이 국가(혹은 언어) 별로 정의된 코드셋을 사용하는 경우가 많았지만 현재는 UTF-8이 많이 사용됩니다. 따라서 ko_KR.UTF-8, en_US.UTF-8, ja_JP.UTF-8과 같이 언어, 지역 표현만 다르고 코드셋은 UTF-8로 정의된 로케일이 자주 사용 됩니다.

이와 달리 POSIX 규격을 따르는 시스템에서 기본값으로 사용되는 POSIX라는 로케일도 있습니다. 이 로케일은 C 로케일이라고도 불립니다. 또한 C 로케일과 UTF-8 코드셋이 조합된 C.UTF-8 로케일이 있습니다. 다음 절에서 확인할 수 있지만 우분투나 데비안의 이미지로 셸을 실행하는 경우 POSIX 로케일이 기본값으로 설정되어 있습니다.

locale 명령어로 컨테이너의 로케일 값 확인

다음 명령어로 우분투 이미지에서 배시 셸Bash Shell을 실행할 수 있습니다.*

* 먼저 우분투(ubuntu:latest)를 중심으로 로케일 설정하는 방법에 대해서 설명합니다. 우분투는 데비안 계열의 리눅스 배포판으로 분류되지만 로케일 설정이 적용되는 방식에는 약간의 차이가 있습니다. 이 차이에 대해서는 뒤에서 다룹니다.

$ docker run -it ubuntu:latest bash
root@4d4524c460fc:/# 

새로운 프롬프트는 우분투 이미지 기반의 컨테이너에서 실행된 배시 셸입니다. 셸을 사용하는 데는 문제가 없습니다. 하지만 이 상태에서 한글을 입력하는 경우 입력이 되지 않을 뿐만 아니라, 입력한 내용이 제대로 해석되지 않아 의도한대로 동작하지 않을 것입니다.

locale 명령어를 사용해 현재 우분투 컨테이너의 로케일 설정을 확인할 수 있습니다.

root@4d4524c460fc:/# locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

이 명령어로 로케일에 관련된 다양한 환경변수의 값을 확인할 수 있습니다. LANG, LANGUAGE, LC_ALL은 설정되어 있지 않으며 나머지 값은 POSIX로 설정되어 있습니다.* 이 로케일 설정 때문에 한글을 입력할 수 없습니다. 현재 시스템에서 사용가능한 로케일 역시 locale 명령어로 확인할 수 있습니다. -a 옵션을 붙여 실행해봅니다.

* 각 변수의 자세한 설명에 대해서는 man locale으로 확인할 수 있습니다.

root@4d4524c460fc:/# locale -a
C
C.UTF-8
POSIX

C, POSIX, C.UTF-8 세 가지 로케일이 등록되어있는 걸 알 수 있습니다. 이 중에서 C와 POSIX가 같다고 보면 실제로는 C와 C.UTF-8 두 가지 로케일이 사용가능합니다. ko_KR.UTF-8과 같은 로케일은 보이지 않습니다. 이러한 로케일을 사용하고자 한다면 별도로 설치를 해야합니다.

하지만 언어와 지역을 포함한 로케일을 사용하지 않더라도 UTF-8 코드셋을 사용할 수 있다면 한글 입력이 가능하지 않을까 유추해볼 수 있습니다. 즉, 목록에 보이는 C.UTF-8 로케일을 사용하면 가장 간단하게 한글을 입력할 수 있도록 설정할 수 있습니다.

간단한 해결책: LC_ALL 환경변수에서 C.UTF-8 로케일 사용

C.UTF-8 로케일을 사용하는 법은 정말 간단합니다. 도커를 실행할 때 LC_ALL이나 LANG 환경변수 값에 C.UTF-8을 넘겨주면 됩니다. 둘 중 하나를 설정하면 다른 로케일 관련 변수들이 모두 해당하는 값으로 지정됩니다.*

* LANGLC_ALL가 동시에 설정되어있을 경우 LC_ALL 값이 우선적으로 사용 됩니다.

$ docker run -it -e LC_ALL=C.UTF-8 ubuntu bash
root@77053c18f4af:/#

로케일을 확인해봅니다.

root@77053c18f4af:/# locale
LANG=
LANGUAGE=
LC_CTYPE="C.UTF-8"
LC_NUMERIC="C.UTF-8"
LC_TIME="C.UTF-8"
LC_COLLATE="C.UTF-8"
LC_MONETARY="C.UTF-8"
LC_MESSAGES="C.UTF-8"
LC_PAPER="C.UTF-8"
LC_NAME="C.UTF-8"
LC_ADDRESS="C.UTF-8"
LC_TELEPHONE="C.UTF-8"
LC_MEASUREMENT="C.UTF-8"
LC_IDENTIFICATION="C.UTF-8"
LC_ALL=C.UTF-8

LANGLANGUAGE 값을 제외한 모든 값이 C.UTF-8로 덮어쓰여진 것을 확인할 수 있습니다. 이 상태에서는 정상적으로 한글 입력이 가능합니다.

root@251a52f0bea2:/# echo "안녕하세요! 여기는 우분투 컨테이너입니다."
안녕하세요! 여기는 우분투 컨테이너입니다.

일본어도 입력 가능합니다.

root@251a52f0bea2:/# echo "こんにちは. ここはコンテナーの中です."
こんにちは. ここはコンテナーの中です.

로케일 설정이 아닌 단순히 CJK나 언어 입력이 필요한 경우라면 이처럼 LC_ALLC.UTF-8로 설정해주면 정상적으로 한글을 입출력할 수 있습니다.

이 내용을 Dockerfile로 만들면 한글 입력이 가능한 우분투 이미지를 만들 수 있습니다.

FROM ubuntu:latest
ENV LC_ALL=C.UTF-8

이로써 한글(CJK) 입력 문제가 간단히 해결되었습니다. 😂

노트
LANGUAGE 환경변수

LANGUAGE 환경변수를 설정하면 애플리케이션을 실행할 때 출력되는 언어를 설정할 수 있습니다. 설정 값은 언어값을 지정하고 :을 구분자로 여러개를 지정할 수 있습니다. 먼저 LANGUAGE 환경변수를 지정하지 않고 apt-get을 실행해보겠습니다.

$ docker run -it ubuntu:latest
root@2542172b9eca:/# apt-get install
Reading package lists... Done
Building dependency tree
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

메시지가 영어로 출력됩니다. 다음으로 LC_ALLLANGUAGE 환경변수를 지정하고 apt-get을 실행해봅니다.

root@2542172b9eca:/# export LC_ALL=C.UTF-8
root@2542172b9eca:/# export LANGUAGE=ko
root@2542172b9eca:/# apt-get install
패키지 목록을 읽는 중입니다... 완료
의존성 트리를 만드는 중입니다
상태 정보를 읽는 중입니다... 완료
0개 업그레이드, 0개 새로 설치, 0개 제거 및 0개 업그레이드 안 함.

한글로 메시지가 출력 됩니다.

ko_KR.UTF-8 로케일 추가 및 설정

우분투 이미지에 기본적으로 포함되어있는 C.UTF-8 로케일을 사용해서 한글 입력에 성공했습니다. 하지만 로케일 설정은 애플리케이션의 작동 방식에 영향을 줄 수 있으므로, 필요에 따라서 정확한 로케일 설정이 필요할 수도 있습니다. 여기선 ko_KR.UTF-8 로케일을 설치하고 설정하는 방법을 알아보겠습니다.

로케일을 추가하기 위해서는 locales 패키지를 설치해야합니다. 새로운 컨테이너를 실행하고 apt-get 명령어로 패키지를 설치합니다.

root@19849e1006ee:/# docker run -it ubuntu bash
root@251a52f0bea2:/# apt-get update
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]
...
root@251a52f0bea2:/# apt-get install locales
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  locales
0 upgraded, 1 newly installed, 0 to remove and 5 not upgraded.
...

locales 패키지를 설치하면 localedef, locale-gen, update-locale와 같은 로케일 관련 명령어들을 사용할 수 있습니다. 먼저 localedeflocale-gen 명령어를 사용하면 원하는 로케일을 생성할 수 있습니다. 생성 가능한 로케일은 /usr/share/i18n/SUPPORTED 파일에서 확인할 수 있습니다.

root@19849e1006ee:/# cat /usr/share/i18n/SUPPORTED
...
ko_KR.UTF-8 UTF-8
ko_KR.EUC-KR EUC-KR
...

ko_KR의 경우 UTF-8 코드셋을 사용하는 로케일과 EUC-KR을 사용하는 로케일이 정의되어있는 것을 확인할 수 있습니다. 먼저 localedef를 사용해 로케일을 생성해보겠습니다. 옵션과 형식은 아래 명령어를 참고해주시기 바랍니다.

root@19849e1006ee:/# localedef -f UTF-8 -i ko_KR ko_KR.UTF-8

locale-gen을 사용하면 좀 더 쉽게 원하는 로케일을 생성할 수 있습니다.

root@19849e1006ee:/# locale-gen ko_KR.UTF-8

locale-gen은 내부적으로 localedef를 사용해 로케일을 정의하므로 어느 쪽을 사용하더라도 결과는 같습니다. locales 로케일이 정상적으로 추가되었는지 확인해봅니다.

root@19849e1006ee:/# locale -a
C
C.UTF-8
POSIX
ko_KR.utf8

update-locale을 사용하면 /etc/default/locale을 설정할 수 있습니다. 하지만 이 설정은 도커로 셸을 실행하는 경우 제대로 적용이 되지 않습니다. 이 파일을 읽어오도록 추가적인 설정이 필요한 것으로 보입니다. 앞서 C.UTF-8을 설정했던 방법과 마찬가지로 LC_ALL 환경변수를 정의해보겠습니다.

root@19849e1006ee:/# export LC_ALL=ko_KR.UTF-8

locale로 현재 로케일 설정을 확인해봅니다.

root@19849e1006ee:/# locale
LANG=
LANGUAGE=
LC_CTYPE="ko_KR.UTF-8"
LC_NUMERIC="ko_KR.UTF-8"
LC_TIME="ko_KR.UTF-8"
LC_COLLATE="ko_KR.UTF-8"
LC_MONETARY="ko_KR.UTF-8"
LC_MESSAGES="ko_KR.UTF-8"
LC_PAPER="ko_KR.UTF-8"
LC_NAME="ko_KR.UTF-8"
LC_ADDRESS="ko_KR.UTF-8"
LC_TELEPHONE="ko_KR.UTF-8"
LC_MEASUREMENT="ko_KR.UTF-8"
LC_IDENTIFICATION="ko_KR.UTF-8"
LC_ALL=ko_KR.UTF-8

정상적으로 ko_KR.UTF-8 로케일이 설정된 것을 확인할 수 있습니다. 단, 현재 셸에서는 한글 입력이 되지 않을 것입니다. 한글 입력이 가능하게 하려면 셸을 새로 실행할 필요가 있습니다. LC_ALL 환경변수를 지정해 셸을 새로 실행해봅니다.

root@19849e1006ee:/# LC_ALL=ko_KR.UTF-8 bash
root@19849e1006ee:/# echo "안녕하세요."
안녕하세요.

이제 정상적으로 한글 입출력이 가능합니다.

ko_KR.UTF-8 로케일이 설정된 도커 이미지 용 Dockerfile 작성

앞에서는 우분투 컨테이너 안에서 직접 ko_KR.UTF-8 로케일을 설정해보았습니다. 지금까지 작업한 내용을 Dockerfile로 만들고 로케일이 미리 설정되어있는 우분투 이미지를 생성해보겠습니다. 아래 내용을 Dockerfile로 저장합니다.

FROM ubuntu:latest
RUN apt-get update && apt-get install -y locales
RUN locale-gen ko_KR.UTF-8
ENV LC_ALL ko_KR.UTF-8

이미지를 빌드합니다. 이름은 ubuntu:ko_KR.UTF-8로 붙이겠습니다.

$ docker build -t ubuntu:ko_KR.UTF-8 .
...
Successfully built d4ffef57a67c
Successfully tagged ubuntu:ko_KR.UTF-8

성공적으로 로케일이 설정된 이미지가 생성되었습니다. 실제로 잘 동작하는 지 확인하기 위해 이 이미지를 기반으로 컨테이너를 실행해봅니다.

$ docker run -it ubuntu:ko_KR.UTF-8 bash
root@293f479e914f:/# locale
LANG=
LANGUAGE=
...
LC_ALL=ko_KR.UTF-8
root@293f479e914f:/# echo "안녕하세요."
안녕하세요.

정상적으로 한글 입출력이 되는 것을 확인할 수 있습니다.

데비안에서 로케일 설정할 때 주의해야할 점

우분투와 데비안의 로케일 설정 방법은 기본적으로 같습니다. 하지만 로케일을 생성할 때 약간의 차이가 있습니다. 데비안의 경우 다양한 공식 이미지에서 사용되고 있기 때문에 차이를 정확히 이해하는 것이 중요합니다.

먼저 locale-gen 스크립트를 실행하는 방법이 다릅니다. 우분투에서는 locale-gen을 실행할 때 생성하고자 하는 로케일 값을 인자로 넘겨줍니다. 하지만 데비안에서는 로케일 값을 인자로 넘겨주더라도 이 로케일이 생성되지 않습니다.

$ docker run -it debian:latest
root@0e8502bbb0c0:/# apt-get update; apt-get install locales
root@0e8502bbb0c0:/# locale-gen ko_KR.UTF-8
Generating locales (this might take a while)...
Generation complete.
root@0e8502bbb0c0:/# locale -a
C
C.UTF-8
POSIX

데비안에서 locale-gen 인자로 넘겨받은 로케일을 생성하는 대신 /etc/locale.gen 파일에 있는 로케일을 생성해줍니다. 이 파일에서 생성하고자 하는 로케일을 언코멘트 하고 locale-gen을 실행하면 로케일이 생성됩니다.

root@0e8502bbb0c0:/# sed -i 's/^# \(ko_KR.UTF-8\)/\1/' /etc/locale.gen
root@0e8502bbb0c0:/# locale-gen
Generating locales (this might take a while)...
  ko_KR.UTF-8... done
Generation complete.

데비안에서는 locale-gen 대신 localedef를 사용하면 좀 더 명확하게 로케일을 생성할 수 있습니다.

$ localedef -f UTF-8 -i ko_KR ko_KR.UTF-8

마치며

여기까지 로케일과 도커 이미지에서 로케일을 다루는 기본적인 방법에 대해서 알아보았습니다. 로케일 문제는 리눅스를 사용하면서 자주 접하게 되는 문제 중 하나입니다. 이런 문제를 쫓아가다보면 여러가지 패키지를 설치하거나 알 수 없는 명령어들을 한가득 실행하고 나면 마법 같이 해결되는 경우가 적지 않습니다. 하지만 로케일의 기본적인 개념만 이해해도 대부분의 문제를 해결하기에는 충분할 것입니다.

더 읽을거리

44BITS 로고

컨테이너란? 리눅스의 프로세스 격리 기능

🏷️ 키워드, 2020-01-23 - 리눅스 컨테이너는 운영체제 수준의 가상화 기술로 리눅스 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술을 의미합니다. 하드웨어를 가상화하는 가상 머신과 달리 커널을 공유하는 방식이기 때문에 실행 속도가 빠르고, 성능 상의 손실이 거의 없다는 장점이 있습니다.
44BITS 로고

로케일(Locale)이란? 국가 및 언어 설정

🏷️ 키워드, 2021-03-07 - 로케일(Locale)은 사용자 인터페이스에서 사용되는 언어, 지역 설정, 출력 형식 등을 정의하는 문자열입니다. 유닉스, 리눅스, 맥OS 등 POSIX (호환) 시스템에서는 언어, 국가, 인코딩 방법의 조합으로된 문자열을 사용합니다.
도움이 되셨나요?
RSS 리더 피들리에서 최신 글을 구독할 수 있습니다.
트위터, 페이스북으로 44BITS의 새소식을 전해드립니다.
✔ 44BITS의 다른 활동도 확인해보세요. 다양한 채널에서 만나볼 수 있습니다.
✔ 따뜻한 댓글 하나와 피드백은 큰 힘이 됩니다.

깃허브(GitHub), L4 로드 밸런서 GLB 디렉터 오픈소스로 공개

🗞 새소식, 2018-08-14 - 깃허브(GitHub)에서는 지난 8월 8일 GLB 디렉터(Github Load Balancer Director)를 오픈소스로 공개하였습니다.

장고(Django) 2.2 LTS 릴리스와 주요 변경 사항

🗞 새소식, 2019-04-12 - Django 2.2가 출시되었습니다. 이 글에서는 Django 2.2에 추가된 기능과 바뀐 점을 알아보려 합니다. 릴리스 노트만으로 이해하기가 어려운 기능엔 설명을 조금 보태었습니다.

다시 보는 2019 한국의 주요 IT 개발 컨퍼런스 모음

🗒 기사, 2019-12-23 - 2019년 한 해 동 다양한 IT/개발 컨퍼런스들이 열렸습니다. 그 중에서 영상으로 다시 보기 가능한 컨퍼런스들을 모았습니다. 44bits 개발 세미나, 파이콘 2019, AWS 서밋 서울, 넥슨 개발자 컨퍼런스, DevOps 밋업, if (kakao) dev, 데뷰 2019 등.