룬 리터럴

룬 리터럴 (Rune Literals)

문자 하나를 둘러싼 작은 리터럴, 즉 룬 리터럴은 Go에서 유니코드 문자 하나를 정수 값으로 표현하는 방법이에요. 화면에 보이는 'x' 같은 글자가 실제로는 어떤 정수 값인지, 그리고 백슬래시 이스케이프로 특수 문자나 임의의 값을 어떻게 인코딩하는지 이번 섹션에서 함께 살펴볼게요. 코드 상수와 이스케이프 규칙의 정확한 범위까지 확인해 두면 문자열 리터럴과 연결되어 이해하기 훨씬 쉬워져요.

출처: Go Specification

본문

룬 리터럴은 룬 상수, 즉 유니코드 코드 포인트를 나타내는 정수 값을 표현해요. 표현 방식은 아주 직관적인데요, 작은따옴표(single quote) 안에 문자 하나 이상을 넣는 거예요. 'x''\n'처럼 말이죠. 따옴표 안에는 newline과 이스케이프되지 않은 작은따옴표를 제외한 어떤 문자든 들어갈 수 있어요. 작은따옴표로 감싼 문자 하나는 그 문자 자체의 유니코드 값을 나타내고, 백슬래시로 시작하는 여러 문자 시퀀스는 다양한 형식으로 값을 인코딩해요.

가장 단순한 형태는 따옴표 안의 단일 문자 그대로를 나타내는 거예요. 여기서 주의할 점이 하나 있는데요, Go 소스 텍스트는 UTF-8로 인코딩된 유니코드 문자라서, 여러 개의 UTF-8 인코딩 바이트가 하나의 정수 값을 나타낼 수 있어요. 예를 들어 리터럴 'a'는 리터럴 a, 즉 유니코드 U+0061, 값 0x61을 나타내는 단일 바이트를 담고 있어요. 반면 'ä'는 두 바이트(0xc3 0xa4)로 표현되는데, 이는 a-디에레시스(dieresis), 즉 U+00E4, 값 0xe4를 나타내요.

백슬래시 이스케이프 몇 가지를 쓰면 임의의 값을 ASCII 텍스트로 인코딩할 수 있어요. 정수 값을 숫자 상수로 표현하는 방법은 네 가지가 있는데요, \x 뒤에 정확히 두 자리 16진수, \u 뒤에 정확히 네 자리 16진수, \U 뒤에 정확히 여덟 자리 16진수, 그리고 일반 백슬래시 \ 뒤에 정확히 세 자리 8진수가 그것이에요. 각 경우에 리터럴의 값은 해당 진법의 자릿수들이 나타내는 값이 돼요.

이 네 가지 표현이 모두 정수 결과를 내긴 하지만, 각각 허용 범위가 달라요. 8진수 이스케이프는 0 이상 255 이하의 값을 표현해야 하고, 16진수 이스케이프는 구조상 이 조건을 자동으로 만족해요. \u\U 이스케이프는 유니코드 코드 포인트를 나타내므로 그 안에서는 일부 값이 불법이에요. 특히 0x10FFFF를 넘는 값과 서로게이트 절반(surrogate half)이 그렇죠.

백슬래시 뒤에는 특수 값을 나타내는 단일 문자 이스케이프 몇 가지가 있어요:

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000B vertical tab
\\   U+005C backslash
\'   U+0027 single quote  (valid escape only within rune literals)
\"   U+0022 double quote  (valid escape only within string literals)

룬 리터럴에서 백슬래시 뒤에 인식되지 않는 문자가 오는 건 불법이에요. 이 규칙을 정리한 문법(EBNF)은 다음과 같아요:

rune_lit         = "'" ( unicode_value | byte_value ) "'" .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .

마지막으로 실제 예시를 보면서 어떤 게 허용되고 어떤 게 불법인지 한 번에 확인해 볼게요:

'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // rune literal containing single quote character
'aa'         // illegal: too many characters
'\k'         // illegal: k is not recognized after a backslash
'\xa'        // illegal: too few hexadecimal digits
'\0'         // illegal: too few octal digits
'\400'       // illegal: octal value over 255
'\uDFFF'     // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point

허용되는 리터럴을 보면 룬 리터럴이 문자 하나, 혹은 이스케이프로 표현되는 값 하나에 해당한다는 점이 핵심이에요. 문자열처럼 여러 글자가 오는 'aa', 백슬래시 뒤에 정의되지 않은 문자를 쓰는 '\k', 자릿수가 모자라거나 범위를 넘어가는 값 등은 전부 불법 처리돼요.

더 알아보기

  • 룬 리터럴이 나타내는 상수의 개념은 Constants 섹션에서 다뤄요.
  • 문자와 유니코드에 대한 기본 규칙은 Characters 섹션을 참고하세요.
  • 룬 리터럴과 짝을 이루는 텍스트 표현인 String literals도 함께 보면 좋아요.