문자열을 만드는 세 가지 방법
자바스크립트 문자열을 만들 때 쓸 수 있는 구분자는 세 가지다. 이 중 두 개는 사실상 똑같이 동작하지만, 나머지 하나는 훨씬 더 많은 일을 해낸다.
작은따옴표든 큰따옴표든 만들어지는 문자열은 완전히 똑같다. 하나를 골라서 일관되게 쓰면 된다(요즘 코드베이스 대부분은 작은따옴표를 기본으로 쓰거나, 포매터가 알아서 정리하도록 맡긴다). 반면 백틱은 얘기가 달라지는데, 이걸로 감싸면 템플릿 리터럴(template literal) 이 된다. 값 보간이 가능하고 여러 줄에 걸쳐 쓸 수도 있다.
내부적으로는 세 가지 모두 동일한 원시(primitive) 문자열 타입을 만든다. 구분자 선택은 순전히 문법적인 취향 문제다.
이스케이프 시퀀스와 따옴표 섞어 쓰기
문자열 안에서 백슬래시(\)는 이스케이프 시퀀스의 시작을 의미한다. 자주 쓰는 것들은 \n(줄바꿈), \t(탭), \\(백슬래시 자체), 그리고 문자열을 연 것과 같은 따옴표를 안에 넣고 싶을 때 쓰는 \" / \' 정도다.
대부분의 이스케이프는 반대쪽 따옴표를 쓰면 피할 수 있습니다. 'She said "hi" and left.'처럼 쓰면 백슬래시가 필요 없죠. 교조적으로 굴기보다는 실용적으로 갑시다.
템플릿 리터럴: 백틱 문자열의 진짜 매력
템플릿 리터럴(template literal)은 일반 문자열로는 할 수 없는 두 가지를 해줍니다. 첫째, ${...} 문법으로 표현식을 문자열 안에 끼워 넣는 문자열 보간이 가능합니다:
${...} 안에는 단순한 변수뿐 아니라 자바스크립트 표현식이면 뭐든 넣을 수 있습니다. 산술 연산, 함수 호출, 삼항 연산자, 심지어 또 다른 템플릿 리터럴까지 — 전부 가능합니다:
두 번째로, 템플릿 리터럴은 \n 없이도 여러 줄에 걸쳐 작성할 수 있습니다:
소스 코드의 줄바꿈이 그대로 문자열의 줄바꿈으로 들어갑니다. 짧은 레이블 정도가 아니라면, 웬만하면 백틱을 쓰는 게 정답이라고 봐도 됩니다.
문자열 연결보다 훨씬 나은 문자열 보간
템플릿 리터럴이 등장하기 전에는 동적인 문자열을 만들 수 있는 방법이 + 연산자뿐이었습니다:
둘 다 결과는 똑같습니다. 다만 템플릿 리터럴은 완성된 문장을 그대로 읽는 느낌인 반면, + 방식은 따옴표, 공백, 연결 연산자를 하나하나 신경 써야 하죠. +는 기존 문자열에 짧은 값 하나 덧붙일 때만 쓰고, 조금이라도 길어진다면 백틱을 꺼내 쓰는 게 좋습니다.
자바스크립트 문자열은 불변(immutable)이다
문자열을 바꾸는 것처럼 보이는 메서드들도 실제로는 전부 새로운 문자열을 반환합니다:
변경하려면 결과를 어딘가에 할당해야 합니다. 초보자들이 s.replace("a", "b")라고 쓰고 s가 바뀌길 기대하다가 자주 당하는 부분이죠. 바뀌지 않습니다. 자바스크립트에서 문자열은 숫자와 똑같습니다. 값 자체는 바꿀 수 없고, 변수가 새로운 값을 가리키도록 다시 지정할 수 있을 뿐이에요.
실무에서 진짜 쓰는 자바스크립트 문자열 메서드
자바스크립트 문자열 API는 방대하지만, 평소에 손이 가는 건 결국 몇 개로 추려집니다:
외워두면 좋은 몇 가지 포인트:
length는 메서드가 아니라 프로퍼티입니다. 괄호를 붙이지 않아요.replace에 일반 문자열을 넘기면 처음 일치하는 부분만 바뀝니다. 전부 바꾸려면replaceAll을 쓰거나g플래그를 붙인 정규식을 사용하세요.slice(start, end)는 반열린 구간입니다.end위치의 문자는 포함되지 않아요.slice에 음수 인덱스를 넘기면 끝에서부터 세어갑니다. 예를 들어s.slice(-3)은 마지막 세 글자를 돌려줍니다.
문자 인덱싱과 순회
자바스크립트 문자열은 읽기 전용 문자 배열처럼 동작합니다. 인덱스로 접근하거나 순회할 수는 있지만, 특정 위치에 값을 대입하는 건 불가능해요:
for...of는 코드 포인트 단위로 순회하기 때문에 대부분의 유니코드를 올바르게 처리합니다. 반면 인덱스(s[i])로 접근하면 UTF-16 코드 단위 로 순회하게 되는데, 이 경우 이모지나 BMP 바깥 영역의 문자가 중간에서 잘려버릴 수 있습니다. 일반적인 텍스트라면 둘 다 문제없지만, 사용자가 입력하는 값을 다룬다면 for...of나 [...s]를 쓰는 편이 안전합니다.
태그드 템플릿 리터럴 (Tagged Template Literals)
자주 쓰이지는 않지만 알아두면 좋은 형태가 하나 더 있습니다. 바로 태그드(tagged) 템플릿 리터럴인데, 정적인 문자열 조각들과 보간된 값들을 각각 별도의 인자로 받아 함수를 호출하는 방식입니다.
태그 함수는 최종 문자열이 어떤 모습이 될지 결정합니다. HTML 이스케이프 처리, 안전한 SQL 생성, CSS 컴파일, GraphQL 파싱까지 모두 태그 함수로 만들 수 있죠. 사실 이 패턴은 직접 작성하기보다는 styled-components, graphql-tag, lit-html 같은 라이브러리에서 훨씬 자주 만나게 됩니다. 그래도 직접 문자열을 가공해야 하는 상황이 오면, tagged template literal만큼 깔끔한 도구도 없습니다.
자주 하는 실수들
자바스크립트 문자열을 다루다 보면 흔히 걸려 넘어지는 부분들을 정리해 봤습니다.
- 메서드 결과를 변수에 담지 않는 실수.
s.trim()만 호출하면 아무 일도 일어나지 않습니다. +로 숫자와 문자열을 이어붙이는 실수."Age: " + 30 + 5의 결과는"Age: 35"가 아니라"Age: 305"입니다. 템플릿 리터럴을 쓰면 이런 문제가 깔끔히 해결되죠:`Age: ${30 + 5}`.==와 문자열 비교를 헷갈리는 경우. 동등성 비교는 나중에 따로 다루겠지만,"1" == 1은true이고"1" === 1은false입니다. 가급적===를 쓰세요.length가 곧 글자 수라고 착각하는 것. 이모지처럼 surrogate pair로 이루어진 문자는"".length가 1이 아니라 2로 나옵니다. 사용자가 실제로 인식하는 글자 수가 필요하다면[..."".length]나Array.from(s).length를 쓰세요.
다음: 숫자와 BigInt
문자열이 원시 타입 중 하나라면, 숫자도 또 다른 원시 타입입니다. 자바스크립트의 숫자 체계에는 나름의 독특한 구석이 있는데요. 정수든 실수든 Number 하나로 처리하고, 그걸로 부족할 때를 위해 BigInt가 따로 있습니다. 이 이야기는 다음 페이지에서 이어집니다.
자주 묻는 질문
자바스크립트에서 따옴표와 백틱은 어떻게 다른가요?
작은따옴표(')와 큰따옴표(")는 둘 다 일반 문자열을 만들고, 서로 바꿔 써도 문제없습니다. 반면 백틱(`)은 템플릿 리터럴을 만들어서 ${...} 문법으로 값을 끼워 넣을 수 있고, 이스케이프 없이 여러 줄도 그대로 쓸 수 있어요. 변수를 넣거나 줄바꿈이 필요한 상황이라면 무조건 백틱이 편합니다.
자바스크립트 문자열 안에 변수를 어떻게 넣나요?
템플릿 리터럴을 쓰면 됩니다. 백틱으로 문자열을 감싸고 ${...} 안에 원하는 식을 넣으면 돼요. 예를 들어 `Hello, ${name}!`은 name의 값을 평가해서 그 자리에 넣습니다. ${a + b}, ${user.name.toUpperCase()}처럼 어떤 식이든 가능하고, 템플릿 리터럴을 중첩해서 쓸 수도 있습니다.
자바스크립트 문자열은 불변(immutable)인가요?
맞습니다. toUpperCase, slice, replace 같은 모든 문자열 메서드는 원본을 바꾸지 않고 새 문자열을 반환합니다. s.toUpperCase()만 호출하고 결과를 어디에도 할당하지 않으면 아무 일도 일어나지 않아요. 파이썬이나 자바의 문자열과 똑같은 동작 방식이라고 보면 됩니다.
태그드 템플릿 리터럴(tagged template)은 뭔가요?
태그드 템플릿은 템플릿의 문자열 조각과 ${} 안의 값들을 인자로 받아서 함수를 호출하는 방식입니다. tag`Hello, ${name}`은 내부적으로 tag(["Hello, ", ""], name)처럼 호출되죠. styled-components나 graphql-tag 같은 라이브러리가 이 방식으로 템플릿을 파싱하거나 가공해서 최종 결과를 만들어냅니다.