7. 여러 개 모아두기 (배열)/7.4 배열 조작하기

7.4.2 배열에서 필요한 부분만 복사해서 가져오기 - `slice()`

thejavascript4kids 2025. 7. 9. 02:40

📘 7.4.2 배열에서 필요한 부분만 복사해서 가져오기 - slice()

어느 늦은 오후, 도서관에서 두꺼운 책을 읽고 있던 중에 특별히 인상 깊은 몇 페이지를 발견했다고 상상해보세요. 그 부분을 친구와 나누고 싶지만, 책 전체를 빌려줄 수는 없어요. 그럴 때 우리는 복사기를 찾아가서 필요한 페이지만 조심스럽게 복사하죠. 원본 책은 그대로 두고 말이에요.

우리가 지난 시간에 배운 splice()는 원본 배열을 직접 수정하는 강력한 도구였어요. 하지만 때로는 원본을 그대로 간직하며, 필요한 부분만 조용히 복사해가고 싶을 때가 있어요. 바로 그런 순간을 위해 slice()가 우리 곁에 있습니다.

🧠 새로운 단어들과 친해지기

새로운 개념을 이해하는 첫걸음은 그 개념이 품고 있는 단어들과 친해지는 것이에요. 마치 새로운 친구를 만날 때 그 친구의 이름을 기억하는 것처럼요.

단어 쉬운 설명
slice() 배열에서 원하는 부분만 복사해서 새로운 배열을 만드는 기능이에요
시작 위치 복사를 시작할 배열의 자리 번호예요. 이 자리부터 복사가 시작돼요
끝 위치 복사를 멈출 배열의 자리 번호예요. 이 자리는 복사에 포함되지 않아요
복사본 slice()로 만들어진 새로운 배열이에요. 원본과는 다른 배열이에요
비파괴적 원본 배열을 전혀 건드리지 않는다는 뜻이에요. 정말 안전해요!

slice라는 단어는 영어로 "얇게 자르다"를 의미해요. 하지만 이 함수는 실제로 무언가를 자르지 않아요. 대신 우리가 원하는 부분을 마치 얇은 종이처럼 조심스럽게 복사해서 가져다주는 거예요. 원본은 전혀 손상시키지 않으면서요.

slice()가 뭔지 알아보기

slice()는 배열에서 가장 친절한 도우미 중 하나예요. 이 함수의 가장 아름다운 특징은 원본을 절대 건드리지 않는다는 거예요. 마치 자연을 사랑하는 사진작가가 꽃을 꺾지 않고 카메라로만 그 아름다움을 담아가는 것처럼요.

지난 시간에 배운 splice()가 원본 배열을 직접 수정하는 것과는 대조적으로, slice()는 원본을 그대로 두고 필요한 부분만 복사해서 새로운 배열을 만들어줘요. 이런 방식을 비파괴적(non-destructive)이라고 부르는데, 원본을 파괴하지 않는다는 뜻이에요.

slice()의 또 다른 특징은 놀라울 정도로 유연하다는 거예요. 우리가 원하는 어떤 부분이든 정확히 복사할 수 있어요. 처음부터 중간까지, 중간부터 끝까지, 심지어 배열의 뒤쪽에서부터 세어서 복사하는 것도 가능해요.

중요한 점 하나를 꼭 기억해야 해요. 끝 위치는 복사에 포함되지 않는다는 거예요. slice(1, 4)라고 하면 1번째, 2번째, 3번째는 복사되지만 4번째는 복사되지 않아요. 마치 "1번부터 4번 전까지"라고 이해하시면 돼요.

재미있는 비유: 포토카피 가게에서 문서 복사하기

slice()를 더 쉽게 이해하기 위해 하나의 이야기를 들려드릴게요.

어느 날 중요한 연구 자료를 담은 두꺼운 문서를 들고 동네 포토카피 가게에 들어갔어요. 전체 문서는 20페이지나 되는데, 동료에게는 5페이지부터 10페이지까지의 내용만 보여주고 싶었어요.

포토카피 기계 앞에서 문서를 조심스럽게 놓고 말했어요. "5페이지부터 9페이지까지만 복사해주세요." (10페이지는 포함되지 않으니까 9페이지까지만이에요.) 복사기는 정확히 그 부분만 읽어서 새로운 문서로 만들어줬어요.

가장 놀라운 점은 원본 문서가 전혀 손상되지 않는다는 거예요. 복사기는 빛으로만 내용을 읽어서 새로운 종이에 인쇄할 뿐, 원본은 그대로 온전하게 보존되어 있어요.

이것이 바로 slice()가 하는 일이에요. 배열이라는 문서에서 우리가 원하는 부분만 안전하게 복사해서 새로운 배열로 만들어주는 정확하고 신뢰할 수 있는 복사기인 거예요.

🎯 왜 slice()를 사용할까요?

slice()를 사용하는 이유들을 하나씩 생각해보면, 우리의 일상과 참 많이 닮아있어요.

가장 중요한 이유는 원본 데이터를 안전하게 보호하기 위해서예요. 중요한 정보가 담긴 배열을 다른 곳에서 사용해야 할 때, slice()로 복사본을 만들어서 사용하면 원본은 절대 손상되지 않아요. 마치 소중한 사진의 원본을 금고에 보관하고 복사본만 액자에 넣어 전시하는 것처럼요.

두 번째는 필요한 부분만 골라서 사용하기 위해서예요. 일주일간의 일기에서 주말 부분만 친구에게 보여주고 싶을 때, 전체 배열에서 주말에 해당하는 부분만 복사해서 사용할 수 있어요.

세 번째는 페이지를 나누어 보여줄 때 정말 유용해요. 긴 목록을 한 번에 다 보여주기엔 너무 많을 때, 페이지별로 나누어 보여주는 기능을 만들 수 있어요. 첫 번째 페이지에는 처음 10개, 두 번째 페이지에는 다음 10개 이런 식으로요.

마지막으로 작업 전 백업을 만들 때도 slice()가 큰 도움이 되어요. 복잡한 작업을 시작하기 전에 전체 배열을 복사해두면, 문제가 생겼을 때 언제든 원래 상태로 돌아갈 수 있어요.

⚙️ slice() 사용법 배우기

slice()의 사용법은 간단하면서도 다양한 변화가 가능해요. 마치 피아노의 기본 음계를 익히면 다양한 곡을 연주할 수 있는 것처럼요.

// 기본 모양
let 새배열 = 원본배열.slice(시작위치, 끝위치);

// 전체 복사하기 (아무것도 안 쓰기)
let 전체복사본 = 원본배열.slice();

// 어떤 위치부터 끝까지 복사하기
let 나머지 = 원본배열.slice(시작위치);

// 뒤에서부터 복사하기 (음수 사용)
let 뒤쪽부분 = 원본배열.slice(-개수);

중요한 규칙들:

  • 시작위치: 복사를 시작할 자리 번호 (이 자리는 포함돼요)
  • 끝위치: 복사를 멈출 자리 번호 (이 자리는 포함되지 않아요!)
  • 음수: 배열 뒤에서부터 세는 특별한 번호
  • 원본 보호: 원본 배열은 절대 바뀌지 않아요

실제 예시:

let fruits = ["사과", "바나나", "오렌지", "포도", "딸기"];

// 여러 가지 복사 방법들
let 중간부분 = fruits.slice(1, 3);    // ["바나나", "오렌지"] (1,2번째만)
let 전체 = fruits.slice();             // 모든 과일 복사
let 뒤에서2개 = fruits.slice(-2);      // ["포도", "딸기"] (뒤에서 2개)

// 원본은 그대로!
console.log(fruits);  // ["사과", "바나나", "오렌지", "포도", "딸기"]

🧪 직접 해보면서 배우기

이제 실제 예시들을 통해 slice()가 품고 있는 가능성을 하나씩 발견해보겠어요. 각각의 예시는 우리가 실제로 마주할 수 있는 일상의 순간들이에요.

🔹 첫 번째 예시: 우리 반 학생들을 조별로 나누기

새 학기가 시작되고, 담임선생님이 조별 활동을 위해 학생들을 여러 조로 나누려고 하세요. 전체 학생 명단은 그대로 보존하면서, 각 조별로 명단을 만들어야 하는 상황이에요.

// 우리 반 전체 학생 명단이에요
let ourClass = ["김민수", "이지은", "박준호", "최서연", "정다영", "강태민", "한예린"];

console.log("=== 우리 반 전체 학생들 ===");
console.log("전체 명단:", ourClass);
console.log("총 학생 수:", ourClass.length + "명");

// 1조를 만들어볼게요 (1번째부터 3번째 학생까지)
let team1 = ourClass.slice(1, 4);
console.log("\n=== 1조 명단 ===");
console.log("1조 학생들:", team1);
console.log("1조 인원:", team1.length + "명");

// 2조를 만들어볼게요 (첫 번째 학생과 나머지 학생들)
let team2Leader = ourClass.slice(0, 1);  // 첫 번째 학생
let team2Members = ourClass.slice(4);     // 4번째부터 끝까지

// 2조 전체를 만들어보기 (배운 방법으로만)
let team2 = [];
team2.push(team2Leader[0]);  // 조장 추가
for (let i = 0; i < team2Members.length; i++) {
    team2.push(team2Members[i]);  // 나머지 조원들 추가
}

console.log("\n=== 2조 명단 ===");
console.log("2조 학생들:", team2);
console.log("2조 인원:", team2.length + "명");

// 원본 명단이 바뀌었는지 확인해볼까요?
console.log("\n=== 원본 확인 ===");
console.log("원본 명단:", ourClass);
console.log("원본도 여전히 " + ourClass.length + "명이에요!");
console.log("slice()가 원본을 안전하게 지켜줬어요!");

이 과정을 통해 우리는 원본 명단을 전혀 건드리지 않으면서도 필요에 따라 다양한 조를 만들 수 있다는 걸 배웠어요. 마치 한 송이 꽃에서 여러 장의 아름다운 사진을 찍을 수 있는 것처럼요.

🔹 두 번째 예시: 일주일 날씨에서 원하는 기간만 가져오기

일기예보를 확인할 때, 때로는 전체 일주일이 아닌 특정 기간의 날씨만 알고 싶을 때가 있어요. 주중 날씨만, 주말 날씨만, 또는 최근 며칠만 보고 싶은 경우 말이에요.

// 월요일부터 일요일까지 일주일 날씨 기록이에요
let weekWeather = ["맑음", "흐림", "비", "맑음", "바람", "눈", "맑음"];
let dayNames = ["월", "화", "수", "목", "금", "토", "일"];

console.log("=== 일주일 날씨 기록 ===");
for (let i = 0; i < weekWeather.length; i++) {
    console.log(dayNames[i] + "요일: " + weekWeather[i]);
}

// 주중 날씨만 복사해보기 (월~금, 0~4번째)
let weekdayWeather = weekWeather.slice(0, 5);
console.log("\n=== 주중 날씨 (월~금) ===");
console.log("주중 날씨:", weekdayWeather);

// 주말 날씨만 복사해보기 (토~일, 5번째부터 끝까지)
let weekendWeather = weekWeather.slice(5);
console.log("\n=== 주말 날씨 (토~일) ===");
console.log("주말 날씨:", weekendWeather);

// 최근 3일 날씨를 복사해보기 (뒤에서 3개)
let lastThreeDays = weekWeather.slice(-3);
console.log("\n=== 최근 3일 날씨 ===");
console.log("최근 3일:", lastThreeDays);

// 전체 날씨를 백업으로 복사해두기
let weatherBackup = weekWeather.slice();
console.log("\n=== 백업 생성 ===");
console.log("백업 완료:", weatherBackup);

// 백업의 독립성 확인해보기
weatherBackup.push("새로운날씨");
console.log("백업본에 추가:", weatherBackup);
console.log("원본은 안전:", weekWeather);
console.log("정말로 다른 배열이에요!");

이 예시를 통해 slice()가 얼마나 유연하고 안전한지 확인할 수 있어요. 음수를 사용해서 뒤에서부터 복사하는 기능도 일상에서 정말 유용하게 사용할 수 있어요.

🔹 세 번째 예시: 성적표에서 상위권과 하위권 분석하기

학급에서 시험이 끝나고 나면, 전체적인 성적 분포를 분석해보고 싶을 때가 있어요. 상위권, 중위권, 하위권 학생들의 점수를 각각 분석하되, 원본 성적표는 그대로 보존해야 하는 상황이에요.

// 우리 반 수학 시험 점수를 높은 순으로 정렬한 목록이에요
let mathScores = [98, 95, 89, 85, 82, 78, 75, 70, 65, 60];

console.log("=== 우리 반 수학 시험 결과 ===");
console.log("전체 점수 (높은 순):", mathScores);
console.log("전체 학생 수:", mathScores.length + "명");

// 상위 3명의 점수를 복사해보기
let topStudents = mathScores.slice(0, 3);
console.log("\n=== 상위 3명 ===");
console.log("상위 3명 점수:", topStudents);

// 하위 3명의 점수를 복사해보기 (뒤에서 3명)
let bottomStudents = mathScores.slice(-3);
console.log("\n=== 하위 3명 ===");
console.log("하위 3명 점수:", bottomStudents);

// 중위권 학생들 점수 복사하기 (3번째부터 7번째까지)
let middleStudents = mathScores.slice(3, 7);
console.log("\n=== 중위권 학생들 ===");
console.log("중위권 점수:", middleStudents);

// 각 그룹의 평균 점수 계산해보기
function calculateAverage(scores) {
    let total = 0;
    for (let i = 0; i < scores.length; i++) {
        total = total + scores[i];
    }
    return total / scores.length;
}

console.log("\n=== 그룹별 평균 점수 ===");
console.log("상위권 평균:", calculateAverage(topStudents) + "점");
console.log("중위권 평균:", calculateAverage(middleStudents) + "점");  
console.log("하위권 평균:", calculateAverage(bottomStudents) + "점");

// 90점 이상 우수 학생들만 복사하기
let excellentStudents = mathScores.slice(0, 2);  // 처음 2명이 90점 이상
console.log("\n=== 우수 학생 (90점 이상) ===");
console.log("우수 학생 점수:", excellentStudents);

// 원본이 안전하게 보존되었는지 확인
console.log("\n=== 원본 안전 확인 ===");
console.log("원본 점수표:", mathScores);
console.log("분석 작업 후에도 " + mathScores.length + "명의 점수가 그대로 보존되었어요!");

이 예시를 통해 slice()가 데이터 분석에 얼마나 유용한지 알 수 있어요. 원본을 안전하게 보호하면서도 다양한 관점에서 데이터를 살펴볼 수 있어요.

🧠 자주 하는 실수와 주의할 점

새로운 도구를 익힐 때는 자주 하는 실수들을 미리 알아두는 것이 중요해요. 실수는 배움의 일부이지만, 미리 알고 있으면 더 부드럽게 배울 수 있어요.

❌ 실수 1: slice()splice() 헷갈리기

이름이 참 비슷해서 많은 사람들이 헷갈려해요. 마치 쌍둥이 형제의 이름을 헷갈리는 것처럼요.

let colors = ["빨강", "파랑", "노랑", "초록"];

console.log("=== 차이점 비교 ===");

// slice() - 안전한 복사 (원본 보존)
let sliceResult = colors.slice(1, 3);
console.log("slice 결과:", sliceResult);      // ["파랑", "노랑"]
console.log("slice 후 원본:", colors);       // ["빨강", "파랑", "노랑", "초록"] (그대로!)

// splice() - 원본 직접 수정
let animals = ["강아지", "고양이", "토끼", "햄스터"];
let spliceResult = animals.splice(1, 2);
console.log("splice 결과:", spliceResult);    // ["고양이", "토끼"]
console.log("splice 후 원본:", animals);     // ["강아지", "햄스터"] (바뀜!)

console.log("\n=== 기억하는 방법 ===");
console.log("slice = 안전한 복사기 (원본 보호)");
console.log("splice = 강력한 편집기 (원본 수정)");

❌ 실수 2: 끝 위치가 포함된다고 생각하기

일상에서 "1번부터 4번까지"라고 하면 보통 4번도 포함한다고 생각하지만, slice()에서는 끝 위치가 포함되지 않아요.

let numbers = [10, 20, 30, 40, 50, 60];

// ❌ 잘못 생각하기: "끝 위치도 포함될 거야!"
let wrongThinking = numbers.slice(2, 4);
console.log("slice(2, 4) 결과:", wrongThinking);   // [30, 40] (4번째 인덱스는 포함 안됨!)

console.log("\n=== 올바른 이해 ===");
console.log("시작 위치 2:", numbers[2], "→ 포함됨");
console.log("끝 위치 4:", numbers[4], "→ 포함 안됨");
console.log("실제 복사된 것:", "2번째와 3번째만");

// ✅ 정확한 사용법
let animals = ["토끼", "다람쥐", "고슴도치", "여우", "늑대"];
let middleAnimals = animals.slice(1, 4);  // 1, 2, 3번째만 포함
console.log("\n1~3번째 동물들:", middleAnimals);  // ["다람쥐", "고슴도치", "여우"]

✏️ 직접 해보기 - 쉬운 연습 문제들

배움은 직접 손으로 만져보고, 눈으로 확인하고, 마음으로 이해할 때 비로소 완성되어요. 이제 우리가 배운 내용을 연습문제를 통해 더욱 견고하게 만들어보겠어요.

Ex1) 요일 배열에서 주중과 주말 복사해보기

// 일주일 요일 배열을 만들어보세요
let days = ["월", "화", "수", "목", "금", "토", "일"];

console.log("전체 요일:", days);

// 주중 요일만 복사해보세요 (월~금, 처음 5개)
let weekdays = days.slice(0, 5);
console.log("주중 요일:", weekdays);

// 주말 요일만 복사해보세요 (토~일, 5번째부터 끝까지)
let weekend = days.slice(5);
console.log("주말 요일:", weekend);

// 원본이 그대로인지 확인해보세요
console.log("원본 요일:", days);
console.log("slice()가 원본을 안전하게 지켜줬어요!");

Ex2) 숫자 배열 전체를 복사해서 백업 만들기

// 중요한 숫자들을 담은 배열이에요
let importantNumbers = [100, 200, 300, 400, 500];

console.log("원본 숫자들:", importantNumbers);

// 전체를 복사해서 백업을 만들어보세요
let backup = importantNumbers.slice();
console.log("백업 숫자들:", backup);

// 백업의 독립성을 확인해보세요
backup.push(600);  // 백업에만 새 숫자 추가
console.log("변경된 백업:", backup);
console.log("안전한 원본:", importantNumbers);
console.log("정말로 다른 배열이에요!");

Ex3) 색깔 배열에서 뒤쪽 일부만 복사해보기

// 무지개 색깔 배열이에요
let rainbowColors = ["빨강", "주황", "노랑", "초록", "파랑", "남색", "보라"];

console.log("무지개 색깔:", rainbowColors);

// 뒤에서 3개 색깔만 복사해보세요
let lastThreeColors = rainbowColors.slice(-3);
console.log("뒤에서 3개 색깔:", lastThreeColors);

// 마지막 색깔만 복사해보세요
let lastColor = rainbowColors.slice(-1);
console.log("마지막 색깔:", lastColor);

// 원본 확인
console.log("원본 무지개:", rainbowColors);

지금까지 slice()의 모든 특성과 활용법을 자세히 알아봤어요. slice()는 원본을 소중히 보호하면서 필요한 부분만 복사할 수 있는 정말 안전하고 유용한 도구예요. 앞으로 배열에서 복사 작업이 필요할 때는 slice()를 자신 있게 사용해보세요.

🔄 복습 문제 - 이전에 배운 내용들 기억하나요?

새로운 것을 배우는 것과 이전에 배운 것을 잊지 않는 것, 이 둘 사이의 균형을 맞추는 것이 진정한 학습이에요. 마치 새로운 친구를 사귀면서도 오래된 친구와의 우정을 소중히 하는 것처럼요.

🔁 5단원 복습: 함수 만들어보기

함수에 대한 기억을 되살려보세요. 함수는 우리가 반복해서 사용할 코드를 하나의 이름으로 묶어준 편리한 도구였어요.

// 문제: 배열에서 가장 큰 수를 찾는 함수를 만들어보세요
function findMaxNumber(numbers) {
    let max = numbers[0];  // 첫 번째 수를 최댓값으로 시작
    for (let i = 1; i < numbers.length; i++) {
        if (numbers[i] > max) {
            max = numbers[i];
        }
    }
    return max;
}

// 사용해보기
let testNumbers = [45, 23, 67, 89, 12];
let maxValue = findMaxNumber(testNumbers);
console.log("가장 큰 수:", maxValue);  // 89

// 화살표 함수로도 만들어보세요
const findMinNumber = (numbers) => {
    let min = numbers[0];
    for (let i = 1; i < numbers.length; i++) {
        if (numbers[i] < min) {
            min = numbers[i];
        }
    }
    return min;
};
console.log("가장 작은 수:", findMinNumber(testNumbers));  // 12

🔁 4단원 복습: for문과 배열 함께 사용하기

반복문도 기억나시나요? 같은 일을 여러 번 해야 할 때 우리를 도와주던 친구였어요.

// 문제: 과일 배열을 for문으로 순서대로 출력해보세요
let fruits = ["사과", "바나나", "오렌지", "포도"];

console.log("과일 목록:");
for (let i = 0; i < fruits.length; i++) {
    console.log((i + 1) + ". " + fruits[i]);
}

// 문제: for문으로 배열을 뒤에서부터 출력해보세요
console.log("\n거꾸로 과일 목록:");
for (let i = fruits.length - 1; i >= 0; i--) {
    console.log((fruits.length - i) + ". " + fruits[i]);
}

✅ 학습 완료 체크리스트

우리가 오늘 함께 걸어온 길을 돌아보며, 각각의 내용을 마음속에 잘 정리했는지 확인해보세요.

학습 내용 이해했나요?
slice()의 기본 개념
기본 사용법과 문법
주요 특징과 차이점
자주 하는 실수들
실전 예제 이해

🎯 추가 연습 문제들

더 깊이 있는 이해를 원하는 여러분을 위해 몇 가지 문제를 더 준비했어요. 이 문제들을 통해 slice()에 대한 이해를 더욱 단단하게 만들어보세요.

추가 문제 1. 과일 배열에서 중간 부분만 복사해보세요.

let fruits = ["사과", "바나나", "오렌지", "포도", "딸기"];

console.log("원본 과일들:", fruits);

// 1번째부터 3번째까지 복사
let middle = fruits.slice(1, 4);
console.log("중간 과일들:", middle);
console.log("원본 확인:", fruits);

추가 문제 2. 숫자 배열 전체를 복사해서 백업본을 만들어보세요.

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

console.log("원본 숫자들:", numbers);

// 전체 복사본 만들기
let backup = numbers.slice();
console.log("백업본:", backup);

// 원본 변경해서 차이 확인
numbers.push(60);
console.log("변경된 원본:", numbers);
console.log("보존된 백업본:", backup);

추가 문제 3. 배열의 뒤쪽 부분만 복사해보세요.

let colors = ["빨강", "파랑", "노랑", "초록", "보라"];

console.log("원본 색깔들:", colors);

// 뒤에서 3개 복사
let lastThree = colors.slice(-3);
console.log("뒤에서 3개:", lastThree);
console.log("원본 길이:", colors.length);

추가 문제 4. slice를 사용해서 배열의 첫 번째와 마지막을 제외한 나머지를 복사해보세요.

let items = ["처음", "둘", "셋", "넷", "마지막"];

console.log("원본 아이템들:", items);

// 첫 번째와 마지막 제외하고 복사
let middle = items.slice(1, -1);
console.log("중간 부분:", middle);

console.log("원본 그대로:", items);

: 위 코드가 정답이에요.

설명: slice(1, -1)을 사용하면 1번째부터 마지막 앞까지 복사되어 첫 번째와 마지막이 제외된 부분만 얻을 수 있어요.

📂 마무리 정보

오늘 우리가 함께 배운 7.4.2의 내용이 여러분의 마음속 지식 정원에 잘 심어졌기를 바라요. slice()라는 도구가 여러분의 프로그래밍 여정에서 든든한 동반자가 되기를 바랍니다.

기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요. 그것이 바로 진정한 학습이 일어나는 방식이니까요.



🚀 더 체계적인 JavaScript 학습을 원하신다면?
이 포스팅에서 다룬 내용을 실제로 실습해보세요!
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
📝 실시간 코드 실행 📊 학습 진도 관리 👥 체계적 커리큘럼
📚 171개 체계적 학습레슨 · 📋 855개 4지선다 연습문제 · 🆓 완전 무료 · ⚡ 즉시 시작