Skip to content
This page has been auto-translated and may contain errors.View in English

문자열

docs.scrimba.com

로그인한 사용자를 위한 인사말을 만든다고 해봅시다. 사용자 이름은 이미 변수에 들어 있습니다. "다시 오신 것을 환영합니다, 지민님"이라는 문구를 만들어야 하는데, "지민"이라는 이름을 직접 타이핑해 넣을 수는 없습니다. 내일은 "수현"일 것이고, 그다음 날은 여러분이 한 번도 들어본 적 없는 이름일 테니까요. 이렇게 그때그때 만들고, 길이를 재고, 형태를 바꿀 수 있는 텍스트가 바로 문자열이며, 이 장에서는 문자열을 다루는 방법을 배웁니다.

문자열 만들기

**문자열**은 텍스트입니다. 인용부호로 감싼 문자들의 나열이라면 모두 문자열입니다. 홑인용부호(')와 겹인용부호(")를 모두 쓸 수 있고 동작 방식도 똑같으니, 하나를 골라 일관되게 사용하면 됩니다.

js
const firstName = "지민";
const greeting = "다시 오신 것을 환영합니다";

문자열을 만드는 가장 유용한 방법은 템플릿 리터럴입니다. 인용부호 대신 백틱()으로 감싸서 씁니다. 템플릿 리터럴 안에서는 ${...}`를 이용해 값을 텍스트에 끼워 넣을 수 있고, JavaScript가 그 자리에 해당 값을 채워줍니다.

js
const firstName = "지민";
const message = `다시 오신 것을 환영합니다, ${firstName}`;
console.log(message); // 다시 오신 것을 환영합니다, 지민

여기서 ${firstName}은 하나의 빈칸입니다. 문자열이 만들어질 때 이 빈칸은 firstName의 값으로 채워집니다. 여러 조각을 이어 텍스트를 만들 때는 템플릿 리터럴을 쓰는 습관을 들이세요.

**문자열**은 텍스트로 이루어진 값입니다. 홑인용부호와 겹인용부호는 완전히 동일한 문자열을 만들어내므로, 둘 중 어느 것을 쓸지는 스타일 문제이거나 어떤 인용부호를 문자열 안에 포함해야 하는지의 문제입니다. 예를 들어 "it's here"는 이스케이프가 필요 없고, 'she said "hi"'도 마찬가지입니다.

기본으로 삼아야 할 형태는 템플릿 리터럴, 즉 백틱으로 감싼 문자열입니다. 일반 인용부호로는 할 수 없는 두 가지가 가능합니다. ${...}로 값을 끼워 넣어 어떤 식이든 그 결과를 텍스트 안에 바로 넣을 수 있고, 특별한 이스케이프 없이 여러 줄에 걸쳐 쓸 수도 있습니다.

js
const firstName = "지민";
const itemCount = 3;
const message = `다시 오신 것을 환영합니다, ${firstName}님. ${itemCount}개의 상품이 대기 중입니다.`;
console.log(message); // 다시 오신 것을 환영합니다, 지민님. 3개의 상품이 대기 중입니다.

빈칸에는 변수 이름만이 아니라 값을 만들어내는 것이라면 무엇이든 들어갈 수 있으므로, ${itemCount} 대신 ${itemCount + 1}을 써도 됩니다. ${...}로 텍스트를 만드는 방식은 인용부호와 더하기 기호로 조각을 이어 붙이는 방식보다 훨씬 깔끔하게 읽히기 때문에 기본으로 쓰이는 것입니다.

문자열은 JavaScript의 텍스트 **원시 타입**입니다. 숫자, 불리언과 함께 이 언어의 기본 값 타입 중 하나입니다(전체 목록은 데이터 타입을 참고하세요). 홑인용부호와 겹인용부호는 서로 바꿔 써도 되고 완전히 같은 값을 만들어내므로, 실제로 고려해야 할 것은 어떤 인용부호를 이스케이프해야 하는지 뿐입니다.

기본으로 선택할 만한 형태는 백틱으로 구분하는 템플릿 리터럴입니다. ${...}로 어떤 식이든 끼워 넣을 수 있으며, 그 안의 내용을 평가한 뒤 결과를 문자열로 강제 변환합니다. 줄바꿈도 그대로 유지되므로 여러 줄짜리 텍스트에 \n이 필요 없습니다.

js
const firstName = "지민";
const itemCount = 3;
const summary = `${firstName}님, ${itemCount}개의 상품이 있고 ${itemCount === 1 ? "대기 중입니다" : "대기 중입니다"}.`;
console.log(summary); // 지민님, 3개의 상품이 있고 대기 중입니다.

빈칸에는 단순한 이름만이 아니라 식이 들어가므로, 산술 연산, 메서드 호출, 비교 연산 모두 그 안에서 동작합니다. 이런 표현력 덕분에 두 조각을 넘어서는 문자열이라면 결합보다 보간이 우세합니다. 최종 텍스트의 모양이 소스 코드에서도 읽기 쉽게 유지되니까요. 여기서 길러야 할 습관은 기본적으로 백틱을 쓰고, 보간이 아무 도움이 되지 않는 고정된 한 조각짜리 문자열에서만 일반 인용부호로 내려가는 것입니다.

Juno문자열 만들기 문자열은 인용부호 안에 든 텍스트이고, 홑인용부호든 겹인용부호든 똑같이 동작하니 하나를 골라 일관되게 쓰면 됩니다. 여러 조각을 이어 텍스트를 만들 때는 템플릿 리터럴을 쓰세요. 백틱과 ${...} 빈칸에 값을 채워 넣는 방식이 가장 자주 쓰는 도구가 될 겁니다.
Juno문자열 만들기 홑인용부호와 겹인용부호는 똑같은 문자열을 만들어내니, 어느 인용부호를 이스케이프 없이 포함해야 하는지에 따라 고르면 됩니다. 다만 기본은 템플릿 리터럴로 하세요. 백틱을 쓰면 ${...}로 어떤 값이든 끼워 넣을 수 있고 여러 줄도 그냥 쓸 수 있습니다. 한번 보간을 써보면 손으로 문자열을 이어 붙이는 게 더 이상 좋아 보이지 않습니다.
Juno문자열 만들기 인용부호는 서로 바꿔 써도 되고, 어느 것을 고를지는 어떤 문자를 이스케이프해야 하는지에 따라 결정됩니다. 기본으로는 백틱을 쓰세요. ${...}는 이름만이 아니라 식 전체를 받으므로 산술 연산과 비교 연산이 텍스트 안에서 바로 동작합니다. 보간이 아무 도움이 안 되는 고정된 한 조각짜리 문자열에는 그냥 인용부호를 쓰면 됩니다.

합치고 길이 재기

두 문자열은 + 연산자로 합칠 수 있습니다. 이를 **연결(concatenation)**이라고 부릅니다.

js
const firstName = "수현";
const greeting = "안녕, " + firstName;
console.log(greeting); // 안녕, 수현

'안녕, ' 안의 쉼표와 공백에 주목하세요. 연결은 작성된 그대로 문자열을 이어붙이므로, 이걸 빼먹으면 안녕,수현처럼 붙어버립니다. 같은 결과를 만들 때 템플릿 리터럴이 보통 더 깔끔하게 읽힙니다: `안녕, ${firstName}`.

모든 문자열은 자기 자신의 길이를 알고 있습니다. .length로 물어보면 됩니다.

js
const firstName = "수현";
console.log(firstName.length); // 2

대괄호를 이용하면 위치로 한 글자씩 읽어올 수도 있는데, 위치는 0부터 셉니다.

js
const firstName = "수현";
console.log(firstName[0]); // 수

첫 번째 글자는 1이 아니라 0 위치에 있습니다. 이렇게 0부터 세는 방식은 JavaScript 전체에서 자주 나오니 지금부터 익숙해지는 게 좋습니다.

두 문자열은 +로 이어붙일 수 있는데, 이 연산을 **연결(concatenation)**이라고 부릅니다. 잘 동작하긴 하지만 금방 지저분해집니다. 공백이나 구분자 하나하나를 인용부호 안에 직접 타이핑해야 하기 때문입니다.

js
const firstName = "수현";
const lastName = "김";
console.log("안녕, " + firstName + " " + lastName); // 안녕, 수현 김

같은 줄을 템플릿 리터럴로 쓰면 `안녕, ${firstName} ${lastName}`이 되는데, 이 때문에 조각이 두 개를 넘어가는 경우에는 보간이 기본값이 됩니다.

문자열은 .length로 자신의 크기, 즉 문자 개수를 알려줍니다.

js
const firstName = "수현";
console.log(firstName.length); // 2

그리고 대괄호로 인덱스를 이용해 한 글자씩 읽을 수 있는데, 인덱스는 0부터 셉니다. 그래서 firstName[0]은 첫 번째 글자이고, 두 글자짜리 문자열이라면 firstName[1]이 마지막 글자입니다.

js
const firstName = "수현";
console.log(firstName[0]); // 수

인덱싱은 0부터 시작합니다. 첫 번째 문자는 위치 0에 있습니다.

문자열은 +로 이어붙이며, 이 연산을 **연결(concatenation)**이라고 부릅니다. 간단하게 두 조각을 합칠 때는 괜찮지만, +는 오버로드되어 있다는 점을 기억하세요. 양쪽 중 하나라도 문자열이면 연결 연산을 하고, 숫자가 섞여 있으면 그 숫자를 문자열로 강제 변환합니다. 그래서 '합계: ' + 3'합계: 3'이 되지만 3 + 36이 됩니다. 템플릿 리터럴은 항상 텍스트를 만들어내므로 이런 모호함을 피할 수 있고, 이것이 가장 단순한 경우를 넘어서면 템플릿 리터럴을 선호하는 이유 중 하나입니다.

.length는 메서드가 아니라 속성이므로 괄호 없이 읽습니다.

js
const label = "김민준";
console.log(label.length); // 3

대괄호를 이용한 인덱싱은 0부터 시작하며 한 글자짜리 문자열을 반환합니다.

js
const label = "김민준";
console.log(label[0]); // 김

한 가지 기억해둘 점: .length는 문자열의 내부 저장 단위 수를 세는 것이지, 사람이 생각하는 "글자" 수와 항상 일치하지는 않습니다. 일반적인 텍스트에서는 둘이 일치합니다. 하지만 일부 이모지나 결합 문자에서는 그렇지 않은데, 눈에 보이는 하나의 기호가 저장 단위 여러 개로 저장될 수 있기 때문입니다. 그래서 '😀'.length2를 반환합니다. 이 문제가 발목을 잡는 일은 드물지만, 이모지가 섞인 텍스트에서 "글자 수"가 이상해 보인다면 바로 이런 이유 때문입니다.

Juno합치고 길이 재기 문자열은 +로 이어붙이지만, 공백을 직접 넣어줘야 한다는 걸 잊지 마세요. 안 그러면 인사말과 이름이 붙어버립니다. 같은 일에는 보통 템플릿 리터럴이 더 깔끔합니다. 문자 수를 셀 때는 .length를, 위치로 한 글자를 읽을 때는 name[0]처럼 대괄호를 쓰세요. 위치는 0부터 셉니다.
Juno합치고 길이 재기+는 연결 연산을 하지만 조각이 두 개를 넘어가면 금방 지저분해지므로, 실제로 쓸 만한 경우에는 템플릿 리터럴이 낫습니다. .length는 속성이라 괄호 없이 문자 수를 알려줍니다. name[0]처럼 인덱싱하면 0부터 시작하므로, 첫 번째 문자는 위치 0에, 마지막 문자는 length에서 1을 뺀 위치에 있습니다.
Juno합치고 길이 재기+는 오버로드되어 있습니다. 문자열과 무엇이든 더하면 연결과 강제 변환이 함께 일어나는데, 이는 은근히 놀라운 결과를 낳는 원인이 되니 템플릿 리터럴에 의존하는 게 낫습니다. .length는 속성이라 괄호가 없습니다. 이건 저장 단위를 세는 것이지 눈에 보이는 글자를 항상 세는 게 아니라서, 이모지 하나가 길이 2로 나올 수도 있습니다. 이 차이는 유니코드에서만 드러나지만, 헷갈리기 전에 알아두면 좋습니다.

유용한 문자열 메서드

문자열에는 내장 **메서드**가 있습니다. 점(.)과 메서드 이름을 써서 문자열에 실행하는 동작들입니다. 가장 자주 쓰게 될 메서드들을 소개합니다.

toUpperCase()toLowerCase()로 대소문자를 바꿉니다.

js
const firstName = "priya";
console.log(firstName.toUpperCase()); // PRIYA

includes()로 한 텍스트가 다른 텍스트 안에 있는지 확인할 수 있고, true 또는 false로 답을 줍니다.

js
const message = "다시 오신 것을 환영합니다";
console.log(message.includes("환영")); // true

slice()로 시작 위치와 끝 위치를 지정해 문자열의 일부를 잘라낼 수 있습니다. 양 끝의 불필요한 공백은 trim()으로 정리할 수 있습니다. 그리고 한 텍스트를 다른 텍스트로 바꿀 때는 replace()를 씁니다.

js
const raw = "  안녕하세요  ";
console.log(raw.trim()); // 안녕하세요

const message = "안녕, 민준";
console.log(message.replace("민준", "유키")); // 안녕, 유키

문자열 메서드는 이보다 훨씬 많이 있고, 전부 외울 필요는 없습니다. 이 다섯 개를 익히고, 나머지는 필요할 때 찾아보면 됩니다.

문자열에는 메서드, 즉 점을 붙여 문자열에서 호출하는 함수들이 딸려 있고, 이 중 몇 개만 알면 일상적인 작업 대부분을 처리할 수 있습니다.

toUpperCase()toLowerCase()는 대소문자를 바꿔주는데, 사용자가 입력한 텍스트를 대소문자 신경 쓰지 않고 비교할 때 유용합니다. includes()는 한 문자열이 다른 문자열 안에 들어 있는지를 불리언으로 반환합니다.

js
const email = "[email protected]";
console.log(email.toLowerCase().includes("@site.com")); // true

slice(start, end)는 시작 인덱스부터 끝 인덱스 바로 앞까지 잘라냅니다. trim()은 양쪽 끝의 공백을 제거하는데, 거의 모든 사용자 입력에 적용하고 싶은 메서드입니다. replace()는 특정 텍스트의 첫 번째 매치를 다른 텍스트로 바꿉니다.

js
const raw = "  order-2024  ";
const clean = raw.trim(); // 'order-2024'
console.log(clean.slice(0, 5)); // order
console.log(clean.replace("order", "invoice")); // invoice-2024

이 다섯 개가 일 잘하는 일꾼들입니다. 이보다 훨씬 많은 메서드가 존재하니 이것들로 처리가 안 될 때는 레퍼런스를 찾아보면 되지만, 대체로는 이 다섯 개에 의존하게 될 겁니다.

문자열에는 방대한 메서드 집합이 있습니다. 점을 붙여 값에 대해 호출하는 함수들입니다. 작업 대부분은 작은 핵심 집합이 처리합니다. 대소문자용 toUpperCase() / toLowerCase(), 불리언 부분 문자열 검사용 includes(), 구간 추출용 slice(start, end)(시작은 포함, 끝은 미포함이고 음수는 끝에서부터 셉니다), 앞뒤 공백 제거용 trim(), 치환용 replace()입니다.

이들을 하나로 묶는 특징이 있습니다. 문자열 메서드는 원본을 절대 바꾸지 않고 새로운 문자열을 반환합니다. 문자열은 **불변(immutable)**입니다. 즉 문자열 값은 그 자리에서 바뀔 수 없으므로, 모든 메서드는 새 문자열을 만들어 건네주고 원본은 그대로 둡니다.

js
const raw = "  이서준 김  ";
const cleaned = raw.trim().toLowerCase();
console.log(cleaned); // 이서준 김
console.log(raw); // '  이서준 김  '  (그대로 유지됨)

결과 자체도 문자열이므로 메서드를 연쇄해서 쓸 수 있습니다. 위 예시의 trim().toLowerCase()가 그 예입니다. 기억해둘 만한 예외 케이스 두 가지가 있습니다. replace()는 모든 매치를 바꿔달라고 요청하지 않는 한 첫 번째 매치만 바꾸고, 끝을 넘어선 인덱싱은 오류가 아니라 undefined를 반환하므로 raw[999]는 오류를 내지 않고 undefined가 됩니다. 이 조용한 undefined는 명확한 오류가 아니라 나중에 헷갈리는 결과로 나타나기 쉬운 종류의 문제입니다.

Juno유용한 문자열 메서드 메서드는 점을 붙여 문자열에 실행하는 동작입니다. toUpperCase(), 예/아니오를 확인하는 includes(), 일부를 잘라내는 slice(), 불필요한 공백을 제거하는 trim(), 텍스트를 바꾸는 replace()가 있습니다. 이 외에도 많으니 이 다섯 개를 익히고 나머지는 필요할 때 찾아보세요.
Juno유용한 문자열 메서드 다섯 개에 의존하세요: 대소문자용 toUpperCase()toLowerCase(), 불리언 검사용 includes(), 추출용 slice(start, end)(끝은 포함되지 않습니다), 사용자 입력용 trim(), 첫 매치 치환용 replace(). 이보다 훨씬 많은 메서드가 있으니 이걸로 부족할 때는 레퍼런스를 찾아보세요. 하지만 이 다섯 개면 대부분의 날들을 감당할 수 있습니다.
Juno유용한 문자열 메서드 핵심 다섯 가지는 toUpperCase/toLowerCase, includes, slice, trim, replace이며, 문자열이 불변이기 때문에 모두 새 문자열을 반환합니다. 그래서 결과를 연쇄할 수 있고 원본은 절대 바뀌지 않습니다. 조심할 함정 두 가지: replace는 기본적으로 첫 번째 매치에만 적용되고, 끝을 넘어선 인덱싱은 오류 대신 undefined를 내놓는데, 이는 나중에 명확한 오류가 아니라 알쏭달쏭한 결과로 드러나는 경향이 있습니다.

문자열, 그다음은

문자열은 JavaScript의 핵심 구성 요소 중 하나이며, 이제 중요한 것들을 다 갖췄습니다. 인용부호나 템플릿 리터럴로 문자열을 만드는 법, 합치고 길이를 재는 법, 그리고 항상 새로운 값을 반환하는 메서드로 형태를 바꾸는 법까지요. 여기서 사용한 점-메서드 패턴은 다른 타입에서도 다시 등장하는데, 특히 배열 메서드에서 리스트를 다루기 시작할 때 그렇습니다.

텍스트는 혼자 다니는 경우가 드뭅니다. 개수나 가격, 합계를 보여주는 순간 숫자와 문자열을 섞어 쓰게 되는데, 템플릿 리터럴은 이 둘을 깔끔하게 엮어주는 방법입니다.