본문 바로가기
프론트엔드/JavaScript

[ JavaScript ] slice, splice, substring, substr 차이점 정리: 기초부터 예제까지

by CODESIGN 2026. 10. 19.

자바스크립트에서 문자열이나 배열의 일부를 가져오려고 하면 비슷한 이름의 메서드들이 등장합니다.

slice(), splice(), substring(), substr()는 이름은 비슷하지만 사용하는 대상과 인수의 의미가 다릅니다. 특히 두 번째 인수가 끝 위치인지, 개수인지와 원본을 변경하는지를 구분하면 이해하기 쉬워집니다.

1. 먼저 알아둘 기초: 인덱스는 0부터 시작한다

인덱스(index)는 문자열의 문자나 배열 요소가 있는 위치입니다.

const text = "ABCDE";

console.log(text[0]); // "A"
console.log(text[1]); // "B"
console.log(text[4]); // "E"
console.log(text.length); // 5
문자 A B C D E
인덱스 0 1 2 3 4

배열도 같습니다.

const fruits = ["사과", "바나나", "포도"];

console.log(fruits[0]); // "사과"
console.log(fruits[1]); // "바나나"
console.log(fruits.length); // 3

따라서 “인덱스 1부터 가져온다”는 말은 두 번째 위치부터 가져온다는 뜻입니다.

2. 네 가지 메서드 한눈에 비교하기

메서드 일반적인 사용 대상 기본 형태 두 번째 인수 원본 변경 반환값
slice() 문자열, 배열 slice(start, end) 끝 인덱스, 미포함 X 추출한 문자열 또는 새 배열
splice() 배열 splice(start, deleteCount, ...items) 삭제할 요소 개수 O 삭제된 요소를 담은 배열
substring() 문자열 substring(start, end) 끝 인덱스, 미포함 X 추출한 문자열
substr() 문자열 substr(start, length) 가져올 길이 X 추출한 문자열

문자열에는 splice()가 없고, 배열에는 substring()과 substr()가 없습니다.

또한 substr()는 레거시 메서드로, 새 코드에서는 slice()나 substring()을 사용하는 편이 좋습니다.

3. slice(): 원본을 유지하면서 일부 가져오기

slice()는 문자열과 배열에서 모두 사용할 수 있습니다. 지정한 구간을 가져오며, 원본을 직접 변경하지 않습니다.

대상.slice(start, end);
  • start: 가져오기 시작할 인덱스, 포함
  • end: 가져오기를 끝낼 인덱스, 미포함

3-1. 문자열에서 사용하기

const text = "ABCDE";
const result = text.slice(1, 4);

console.log(result); // "BCD"
console.log(text);   // "ABCDE"

인덱스 1인 B부터 시작해, 인덱스 4인 E 바로 앞까지 가져옵니다. 따라서 포함되는 인덱스는 1, 2, 3입니다.

끝 인덱스의 문자는 포함하지 않는다는 점을 기억하면 됩니다.

3-2. 끝 인덱스를 생략하면 끝까지 가져온다

const text = "ABCDE";

console.log(text.slice(2)); // "CDE"
console.log(text.slice()); // "ABCDE"

인수를 모두 생략하면 전체 구간을 가져옵니다.

3-3. 음수는 뒤에서부터 위치를 계산한다

const text = "ABCDE";

console.log(text.slice(-1));    // "E"
console.log(text.slice(-2));    // "DE"
console.log(text.slice(0, -1)); // "ABCD"
console.log(text.slice(-4, -1));// "BCD"

문자열 길이가 5라면 -1은 5 - 1 = 4 위치, -2는 5 - 2 = 3 위치를 의미합니다.

slice(0, -1)은 처음부터 마지막 문자 직전까지 가져오므로 마지막 문자를 제외할 때 유용합니다.

3-4. 시작 위치가 끝 위치보다 뒤에 있으면?

const text = "ABCDE";

console.log(text.slice(4, 1)); // ""
console.log(text.slice(2, 2)); // ""

뒤에서 앞으로 읽는 기능은 아닙니다. 계산된 시작 위치가 끝 위치 이상이면 빈 문자열을 반환합니다.

3-5. 배열에서 사용하기

const numbers = [10, 20, 30, 40, 50];
const result = numbers.slice(1, 4);

console.log(result);  // [20, 30, 40]
console.log(numbers); // [10, 20, 30, 40, 50]

배열에서도 끝 인덱스는 포함하지 않습니다.

const numbers = [10, 20, 30, 40, 50];

console.log(numbers.slice(2));  // [30, 40, 50]
console.log(numbers.slice(-2)); // [40, 50]
console.log(numbers.slice());   // [10, 20, 30, 40, 50]

3-6. 배열 전체를 복사할 수 있지만, 얕은 복사다

const original = [10, 20, 30];
const copy = original.slice();

copy.push(40);

console.log(original); // [10, 20, 30]
console.log(copy);     // [10, 20, 30, 40]
console.log(original === copy); // false

서로 다른 배열이므로 복사본에 요소를 추가해도 원본 배열의 길이는 달라지지 않습니다.

하지만 배열 안에 객체가 있다면 그 객체까지 새로 복사하지는 않습니다.

const original = [{ name: "희라" }];
const copy = original.slice();

console.log(original === copy);       // false
console.log(original[0] === copy[0]); // true

copy[0].name = "수정된 이름";

console.log(original[0].name); // "수정된 이름"

배열은 새로 만들어졌지만, 내부 객체는 같은 객체를 가리킵니다. 이것이 얕은 복사(shallow copy)입니다.

4. splice(): 배열을 직접 삭제·추가·교체하기

splice()는 배열의 내용을 직접 변경합니다.

배열.splice(start, deleteCount, item1, item2, ...);
  • start: 변경을 시작할 인덱스
  • deleteCount: 삭제할 요소 개수
  • item1, item2, ...: 그 위치에 추가할 요소

두 번째 인수는 끝 인덱스가 아니라 삭제할 개수입니다.

4-1. 요소 삭제하기

const fruits = ["사과", "바나나", "포도", "딸기"];
const removed = fruits.splice(1, 2);

console.log(fruits);  // ["사과", "딸기"]
console.log(removed); // ["바나나", "포도"]

인덱스 1부터 요소 2개를 삭제합니다. 원본 배열은 바뀌고, 반환값에는 삭제된 요소가 들어갑니다.

4-2. 삭제 없이 요소 추가하기

const fruits = ["사과", "포도"];
const removed = fruits.splice(1, 0, "바나나");

console.log(fruits);  // ["사과", "바나나", "포도"]
console.log(removed); // []

삭제할 개수가 0이므로 기존 요소는 삭제하지 않고, 인덱스 1 위치에 바나나를 끼워 넣습니다.

4-3. 요소 교체하기

const fruits = ["사과", "바나나", "포도"];
const removed = fruits.splice(1, 1, "딸기");

console.log(fruits);  // ["사과", "딸기", "포도"]
console.log(removed); // ["바나나"]

요소 1개를 삭제하고 그 자리에 새 요소를 추가한 결과입니다. 삭제하는 개수와 추가하는 개수가 같을 필요는 없습니다.

const letters = ["A", "B", "C"];

letters.splice(1, 1, "X", "Y");

console.log(letters); // ["A", "X", "Y", "C"]

4-4. 삭제할 개수를 생략하면 끝까지 삭제한다

const numbers = [10, 20, 30, 40];
const removed = numbers.splice(2);

console.log(numbers); // [10, 20]
console.log(removed); // [30, 40]

단, splice()처럼 인수를 하나도 전달하지 않으면 아무것도 삭제하지 않습니다.

또한 생략과 명시적인 undefined는 다릅니다.

const numbers = [10, 20, 30];

console.log(numbers.splice(1, undefined)); // []
console.log(numbers); // [10, 20, 30]

두 번째 인수에 undefined를 직접 전달하면 삭제할 개수는 0으로 처리됩니다.

4-5. 음수 시작 위치도 사용할 수 있다

const numbers = [10, 20, 30, 40];
const removed = numbers.splice(-2, 1);

console.log(numbers); // [10, 20, 40]
console.log(removed); // [30]

뒤에서 두 번째 위치부터 요소 1개를 삭제한 것입니다.

4-6. const 배열도 splice()로 변경할 수 있다

const numbers = [10, 20, 30];

numbers.splice(1, 1);

console.log(numbers); // [10, 30]

const는 변수에 다른 값을 다시 대입하는 것을 막습니다. 배열 내부의 요소 변경까지 막는 것은 아닙니다.

5. substring(): 두 위치 사이의 문자열 가져오기

substring()은 문자열에서 사용합니다.

문자열.substring(start, end);

일반적인 양수 인덱스에서는 slice()와 결과가 같습니다.

const text = "ABCDE";

console.log(text.substring(1, 4)); // "BCD"
console.log(text.substring(2));   // "CDE"
console.log(text);               // "ABCDE"

시작 인덱스는 포함하고, 끝 인덱스는 포함하지 않습니다.

다만 slice()와 다른 규칙이 두 가지 있습니다.

5-1. 음수는 0으로 처리한다

const text = "ABCDE";

console.log(text.slice(-2));     // "DE"
console.log(text.substring(-2)); // "ABCDE"

substring(-2)는 substring(0)처럼 동작합니다.

뒤에서부터 위치를 계산하려면 slice()가 더 편리합니다.

5-2. 시작 인덱스가 더 크면 두 값을 바꿔 처리한다

const text = "ABCDE";

console.log(text.slice(4, 1));     // ""
console.log(text.substring(4, 1)); // "BCD"
console.log(text.substring(1, 4)); // "BCD"

substring(4, 1)은 substring(1, 4)와 같은 결과입니다.

음수 처리와 인덱스 교환 규칙이 함께 적용되는 경우도 있습니다.

const text = "ABCDE";

console.log(text.substring(3, -1)); // "ABC"

먼저 -1을 0으로 처리해 substring(3, 0)이 되고, 두 값을 바꿔 substring(0, 3)처럼 동작합니다.

6. substr(): 시작 위치부터 지정한 길이만큼 가져오기

substr()는 문자열에서 사용하며, 두 번째 인수는 끝 위치가 아니라 가져올 길이입니다.

문자열.substr(start, length);

6-1. 같은 숫자를 전달해도 결과가 다르다

const text = "ABCDE";

console.log(text.slice(1, 3));     // "BC"
console.log(text.substring(1, 3)); // "BC"
console.log(text.substr(1, 3));    // "BCD"
코드 의미 결과
slice(1, 3) 인덱스 1부터 3 직전까지 "BC"
substring(1, 3) 인덱스 1부터 3 직전까지 "BC"
substr(1, 3) 인덱스 1부터 3개 "BCD"

6-2. 음수 시작 위치와 길이 생략

const text = "ABCDE";

console.log(text.substr(-3, 2)); // "CD"
console.log(text.substr(2));     // "CDE"
console.log(text.substr(1, 0));  // ""
console.log(text.substr(1, -2)); // ""

음수 시작 위치는 뒤에서부터 계산합니다. 길이를 생략하면 끝까지 가져오고, 길이가 0 이하이면 빈 문자열을 반환합니다.

6-3. 새 코드에서는 slice()나 substring() 사용하기

substr()는 오래된 코드와의 호환을 위한 레거시 기능이며, MDN에서는 사용 중단 권고(Deprecated)로 표시합니다.

예를 들어 시작 위치와 길이가 모두 0 이상의 정수라면 다음처럼 바꿀 수 있습니다.

const text = "ABCDE";
const start = 1;
const length = 3;

// 기존 방식
console.log(text.substr(start, length)); // "BCD"

// 대체 방식
console.log(text.slice(start, start + length)); // "BCD"

다만 음수 시작 위치나 음수 길이가 들어올 수 있는 기존 코드라면, 단순히 위 식으로 바꿨을 때 결과가 달라질 수 있으므로 입력 범위를 확인해야 합니다.

7. 가장 헷갈리는 차이점 비교하기

7-1. slice()와 splice(): 추출과 삭제의 차이

아래 예제들은 각각 새 배열로 시작합니다.

const numbers = [10, 20, 30, 40, 50];
const result = numbers.slice(1, 3);

console.log(result);  // [20, 30]
console.log(numbers); // [10, 20, 30, 40, 50]
const numbers = [10, 20, 30, 40, 50];
const result = numbers.splice(1, 3);

console.log(result);  // [20, 30, 40]
console.log(numbers); // [10, 50]

slice(1, 3)은 인덱스 1부터 3 직전까지 가져오기, splice(1, 3)은 인덱스 1부터 요소 3개 삭제하기입니다.

7-2. 문자열 메서드 결과 비교

아래 표는 text = "ABCDE"를 기준으로 합니다.

전달한 인수 slice substring substr
(1, 3) "BC" "BC" "BCD"
(2) "CDE" "CDE" "CDE"
(-2) "DE" "ABCDE" "DE"
(4, 1) "" "BCD" "E"
(1, -1) "BCD" "A" ""
(2, 2) "" "" "CD"

7-3. 범위를 벗어나면 어떻게 될까?

const text = "ABCDE";

console.log(text.slice(2, 100));     // "CDE"
console.log(text.substring(2, 100)); // "CDE"
console.log(text.substr(2, 100));    // "CDE"

console.log(text.slice(100));     // ""
console.log(text.substring(100)); // ""
console.log(text.substr(100));    // ""

필요한 구간이 문자열 끝을 넘어가면 존재하는 부분까지만 가져옵니다. 위 예제처럼 시작 위치가 끝보다 뒤에 있으면 가져올 내용이 없어 빈 문자열을 반환합니다.

8. 실제 활용 예제

8-1. 문자열 앞부분 가져오기

const date = "2026-10-12";

console.log(date.slice(0, 4)); // "2026"

8-2. 문자열 마지막 부분 가져오기

const phone = "01012345678";

console.log(phone.slice(-4)); // "5678"

8-3. 배열에서 앞의 세 개만 표시하기

const posts = ["글1", "글2", "글3", "글4", "글5"];
const preview = posts.slice(0, 3);

console.log(preview); // ["글1", "글2", "글3"]
console.log(posts);   // ["글1", "글2", "글3", "글4", "글5"]

8-4. 배열 중간에 메뉴 추가하기

const menus = ["홈", "마이페이지"];

menus.splice(1, 0, "상품");

console.log(menus); // ["홈", "상품", "마이페이지"]

8-5. 문자열의 특정 구간 바꾸기

문자열에는 splice()가 없으므로, 앞부분과 뒷부분을 가져와 새로운 문자열을 만들 수 있습니다.

const text = "ABCDE";
const result = text.slice(0, 1) + "XY" + text.slice(3);

console.log(result); // "AXYDE"
console.log(text);   // "ABCDE"

B와 C를 제외하고, 그 자리에 XY를 연결한 예제입니다.

9. 어떤 메서드를 선택하면 좋을까?

하고 싶은 작업 선택할 메서드
문자열의 일부 가져오기 slice() 또는 substring()
뒤에서부터 문자열 가져오기 slice()
두 인덱스의 순서에 상관없이 사이 구간 가져오기 substring()
배열의 일부를 새 배열로 가져오기 slice()
배열을 직접 삭제·추가·교체하기 splice()
기존 코드의 “시작 위치부터 길이만큼 추출” 이해하기 substr()

이름을 외우기보다, 호출 코드를 볼 때 대상이 문자열인지 배열인지, 두 번째 숫자가 위치인지 개수인지, 원본이 바뀌는지를 먼저 확인해 보세요. 같은 숫자를 전달했는데 다른 결과가 나오는 이유를 이해하기가 훨씬 쉬워집니다.

참고 자료

댓글