C-2: 함수 기초
안녕하세요, 홍순구 튜터입니다. 지난 시간 우리는 멈춰 있던 화면에 첫 동작을 만들었어요. 변수에 좋아요 숫자를 담고, 반복문으로 5명이 좋아요를 누르게 하고, 조건문으로 인기 게시물인지 판정하는 좋아요 카운터까지 완성했죠.
그런데 끝에 살짝 답답한 걸 짚었어요. 게시물이 한 개가 아니라 열 개라면? 그 카운터 코드를 게시물마다 복사해서 붙여넣어야 한다면요. js/main.js가 똑같은 코드로 끝없이 길어지겠죠. 글자 하나 고치려면 복사한 데를 전부 찾아다니며 고쳐야 하고요. 같은 코드를 반복해서 쓰는 건 정말 비효율이에요.
오늘은 그 반복을 깔끔하게 묶는 함수(function, 기능 묶음)를 배웁니다. "좋아요 안내하기"라는 동작에 이름을 한 번만 붙여두고, 필요할 때마다 그 이름을 불러서 쓰는 거예요. 한 번 만들면 백 번이고 천 번이고 재사용할 수 있죠.
여기서 반가운 소식 하나. 혹시 오전에 Java로 메서드(method)를 배웠다면, 오늘 배울 함수가 바로 그거예요. 이름을 붙여서 코드를 묶고, 입력을 받아 결과를 돌려주는 — 개념이 완전히 똑같아요. 지난 시간 if와 for가 Java와 판박이였듯, 함수도 오전에 배운 메서드와 같은 도구라고 생각하면 됩니다.
함수가 없으면 함수가 있으면
┌─────────────────────┐ ┌─────────────────┐
│ console.log(...) 복사 │ │ function 안내() {│ ← 한 번만 정의
│ console.log(...) 복사 │ │ console.log() │
│ console.log(...) 복사 │ → │ } │
│ console.log(...) 복사 │ ├─────────────────┤
│ ...끝없이 길어짐 │ │ 안내() 안내() │ ← 이름만 불러 쓰기
└─────────────────────┘ │ 안내() 안내() │
└─────────────────┘
함수는 요리 레시피와 비슷해요. "라면 끓이기" 레시피를 한 번 적어두면, 라면이 먹고 싶을 때마다 처음부터 순서를 다시 쓸 필요 없이 "그 레시피대로!" 한마디면 되잖아요. 함수도 마찬가지로, 한 번 정의해두면 이름만 부르면 그 안의 코드가 그대로 실행돼요.
💡 오늘 수업의 핵심 — "반복되는 코드에 이름을 붙여 함수로 묶고, 입력(매개변수)을 받아 결과(return)를 돌려준다. 같은 일을 매번 복사하지 않고 이름만 불러서 재사용한다." 🎯
🎯 학습 목표
- 함수가 무엇인지 이해하고, 함수 선언식으로 코드를 묶어 이름을 붙입니다.
- 같은 함수를 함수 표현식과 화살표 함수(
=>) 세 가지 형태로 만들고 비교합니다. - 매개변수(입력)와 **반환값(
return, 출력)**의 흐름을 이해하고,console.log(찍기)와return(돌려주기)의 차이를 구분합니다. - 매개변수 기본값으로 값을 안 넘겼을 때의 동작을 정합니다.
- **rest parameter(
...)**로 개수가 정해지지 않은 인자를 하나의 묶음으로 받습니다. - 오늘 만든 함수들을 모아 재사용 가능한 유틸 함수 모음을 완성합니다.
Step 1: 함수란 무엇인가 — 반복 코드를 한 번만 적기
지난 시간 좋아요 카운터를 만들면서, 게시물마다 console.log로 좋아요 개수를 안내하는 코드를 떠올려봅시다. 게시물이 세 개면 이렇게 됐을 거예요.
console.log("좋아요 42개입니다");
console.log("좋아요 8개입니다");
console.log("좋아요 150개입니다");
숫자만 다를 뿐 "좋아요 ○개입니다"라는 틀은 완전히 똑같죠. 게시물이 백 개라면 이 줄을 백 번 복사해야 해요. 게다가 나중에 문구를 "❤️ 좋아요 ○개"로 바꾸고 싶으면, 백 군데를 전부 찾아 고쳐야 하고요. 생각만 해도 끔찍하죠.
이럴 때 쓰는 게 함수예요. "좋아요 안내하기"라는 동작을 딱 한 번만 정의해두고, 숫자만 바꿔가며 부르는 거죠.
함수 정의하고 부르기
// instagram-clone-frontend/js/main.js
function announceLike(count) {
console.log("좋아요 " + count + "개입니다");
}
announceLike(42); // 좋아요 42개입니다
announceLike(8); // 좋아요 8개입니다
announceLike(150); // 좋아요 150개입니다
콘솔 출력은 이래요.
좋아요 42개입니다
좋아요 8개입니다
좋아요 150개입니다
복사한 세 줄과 결과가 똑같죠? 그런데 안내 문구를 적은 console.log는 딱 한 곳에만 있어요. 이게 함수의 힘이에요.
한 줄씩 뜯어보기
새 용어가 세 개 나왔으니 차근차근 볼게요. function으로 시작하는 윗부분이 함수를 정의(define)하는 곳이고, 아래 announceLike(42)처럼 이름을 부르는 게 함수를 호출(call)하는 거예요.
function announceLike(count) { ← 정의 (한 번만)
────┬──── ─┬─
함수 이름 매개변수
console.log("좋아요 " + count + "개입니다");
}
announceLike(42); ← 호출 (필요할 때마다)
──┬── ┬
이름 인자(전달값)
function— "지금부터 함수를 만들 거야"라는 신호예요. 이 형태를 함수 선언식이라고 불러요.announceLike— 함수의 이름이에요. 나중에 이 이름으로 함수를 불러요. 레시피 제목이라고 보면 돼요.count— 함수가 받는 입력이에요. 이걸 매개변수(parameter)라고 불러요. 호출할 때 넘긴 값(42)이 이 자리에 들어와요.
announceLike(42)를 부르면, 42가 count에 담기고, 함수 안의 console.log가 "좋아요 42개입니다"를 출력해요. announceLike(8)을 부르면 이번엔 count가 8이 되고요. 같은 레시피에 재료(숫자)만 바꿔 넣는 거죠.
🙋 학생 질문 — "튜터님, 함수를 정의만 하고 호출을 안 하면 어떻게 되나요?"
아무 일도 안 일어나요. 함수 정의는 "이런 동작을 할 줄 안다"고 등록만 해두는 거예요. 레시피를 적어서 서랍에 넣어둔 셈이죠. 실제로 요리가 되려면 "이 레시피대로 해줘!"라고 불러야 해요. 그게 호출이에요.
그래서 function announceLike(count) { ... }만 적고 끝내면 콘솔엔 아무것도 안 찍혀요. announceLike(42)처럼 이름을 불러야 비로소 안쪽 코드가 실행돼요.
💡 오전에 Java로 메서드를 만들어봤다면,
void announceLike(int count) { ... }가 떠오르죠? JavaScript는function announceLike(count) { ... }예요.void나int같은 타입을 안 써도 된다는 것만 다르고, "코드를 묶어 이름 붙이고 입력을 받는다"는 핵심은 똑같아요.
Step 2: 함수 표현식과 화살표 함수(=>)
방금 만든 함수는 function으로 시작하는 함수 선언식이었어요. 그런데 JavaScript에서 함수를 만드는 방법은 이것 말고도 두 가지가 더 있어요. 똑같이 동작하는 함수를 세 가지 형태로 만들어보면서 비교해봅시다.
함수 표현식 — 변수에 함수를 담기
지난 시간 우리는 변수에 숫자(let likeCount = 42)나 글자(const username = "hong_tutor")를 담았죠. JavaScript에서는 함수 자체도 변수에 담을 수 있어요. 이렇게 변수에 함수를 담는 형태를 함수 표현식이라고 불러요.
// instagram-clone-frontend/js/main.js
const announceLikeExpr = function (count) { // 함수 표현식: 변수에 함수를 담기
console.log("좋아요 " + count + "개입니다");
};
function 뒤에 이름 없이 바로 괄호가 오고, 그 함수 덩어리를 announceLikeExpr이라는 변수에 통째로 담았어요. 호출할 때는 변수 이름을 부르면 돼요. announceLikeExpr(42)처럼요.
화살표 함수 — function 글자를 =>로
함수 표현식을 더 짧게 줄인 게 화살표 함수(arrow function)예요. function이라는 긴 글자를 빼고, 대신 매개변수 뒤에 화살표 =>를 붙이는 거죠.
// instagram-clone-frontend/js/main.js
const announceLikeArrow = (count) => { // 화살표 함수: function 글자를 => 로
console.log("좋아요 " + count + "개입니다");
};
announceLikeExpr(42);
announceLikeArrow(42);
세 형태를 나란히 놓고 보면 변화가 한눈에 보여요.
선언식 function announceLike(count) { ... }
표현식 const x = function (count) { ... };
화살표 const x = (count) => { ... };
└─ function 글자가 사라지고 => 가 등장
announceLikeExpr(42)와 announceLikeArrow(42)를 부르면, 둘 다 똑같이 "좋아요 42개입니다"를 찍어요. 모양만 다를 뿐 하는 일은 같죠.
한 줄 화살표 — 중괄호와 return 생략
화살표 함수의 진짜 매력은 짧은 함수를 더 짧게 줄일 수 있다는 거예요. 함수가 "값 하나를 계산해서 돌려주기"만 한다면, 중괄호 { }와 return을 생략하고 한 줄로 적을 수 있어요.
// instagram-clone-frontend/js/main.js
const doubleLikes = (n) => n * 2; // 한 줄 화살표 (중괄호·return 생략)
console.log(doubleLikes(21)); // 42
doubleLikes는 숫자 하나(n)를 받아서 두 배(n * 2)로 돌려주는 함수예요. 화살표 오른쪽에 바로 적은 n * 2가 곧 돌려줄 값이 돼요. doubleLikes(21)을 부르면 21 * 2인 42가 나오죠. 콘솔 출력은 이래요.
42
지난 시간 마무리에서 "오늘 길게 적은 코드가 다음 시간엔 훨씬 간결해진다"고 약속했죠? 바로 이 화살표 함수 덕분이에요. 여러 줄짜리 함수가 짧은 한 줄로 줄어드는 게 보이시나요.
💡 세 형태 중 뭘 써야 하냐면, 일단 화살표 함수를 기본으로 쓰는 흐름이 요즘 많아요. 짧고 깔끔하거든요. 하지만 함수 선언식도 "이건 함수다"가 한눈에 보여서 여전히 자주 써요. 지금은 "셋 다 같은 함수를 만드는 방법이고, 모양만 다르다"만 기억하면 충분해요.
Step 3: 매개변수와 반환값(return)
함수는 결국 입력을 받아 출력을 내놓는 도구예요. 자판기를 떠올려보세요. 동전(입력)을 넣고 버튼을 누르면 음료수(출력)가 나오죠. 함수도 똑같아요. 매개변수로 값을 받아서, return으로 결과를 돌려줘요.
입력 함수 출력
┌────┐ ┌──────────┐ ┌──────┐
│ 8 │ ──▶ │formatLike │ ──▶ │"8개" │
└────┘ │ Count │ └──────┘
┌────┐ │ │ ┌──────┐
│1240│ ──▶ │ │ ──▶ │"1.2천"│
└────┘ └──────────┘ └──────┘
return으로 결과 돌려주기
좋아요 숫자를 보기 좋게 다듬는 함수를 만들어볼게요. 1000개가 넘으면 "1.2천"처럼 줄이고, 아니면 그냥 "8개"로 보여주는 거예요.
// instagram-clone-frontend/js/main.js
function formatLikeCount(count) {
if (count >= 1000) {
return (count / 1000).toFixed(1) + "천"; // 1240 → "1.2천"
}
return count + "개";
}
console.log(formatLikeCount(8)); // 8개
console.log(formatLikeCount(1240)); // 1.2천
콘솔 출력은 이래요.
8개
1.2천
return은 "이 값을 함수 밖으로 돌려줘"라는 뜻이에요. formatLikeCount(1240)을 부르면, 1240 >= 1000이 참이라 (1240 / 1000).toFixed(1) + "천"을 계산해서 "1.2천"을 돌려줘요. (toFixed(1)은 소수점 첫째 자리까지만 남기라는 명령이에요. 1.24가 "1.2"가 되죠.) 그 돌려받은 값을 console.log가 받아서 화면에 찍는 거고요.
여기서 중요한 게 하나 있어요. return을 만나면 함수는 그 즉시 끝나요. 그래서 1240이 들어왔을 때 첫 번째 return에서 함수가 끝나버려, 아래 return count + "개"는 실행되지 않아요. 반대로 8이 들어오면 if 조건이 거짓이라 건너뛰고, 아래 return count + "개"가 실행돼서 "8개"가 나오죠.
⚠️ console.log(찍기)와 return(돌려주기)는 달라요
여기가 함수를 처음 배울 때 가장 헷갈리는 부분이에요. 둘 다 "결과를 내놓는 것" 같지만, 완전히 다른 일이에요.
console.log("8개") return "8개"
───────────────── ──────────────
화면(콘솔)에 찍고 끝. 값을 함수 밖으로 건네줌.
다시 쓸 수 없음. 다른 곳에서 받아서 쓸 수 있음.
console.log는 값을 화면에 보여주고 그걸로 끝이에요. 손에 아무것도 남지 않아요. 반면 return은 값을 함수 밖으로 건네줘서, 그 값을 변수에 담거나 다른 함수에 넘기는 등 계속 활용할 수 있어요.
예를 들어 formatLikeCount가 console.log로 직접 찍어버렸다면, 그 "1.2천"이라는 결과를 다른 데서 다시 쓸 방법이 없어요. return으로 돌려줬기 때문에 console.log(formatLikeCount(1240))처럼 받아서 쓸 수 있는 거예요. 함수는 보통 결과를 return으로 돌려주는 게 기본이라고 생각하세요.
Step 4: 매개변수 기본값
함수를 부를 때 값을 안 넘기면 어떻게 될까요? 인사하는 함수를 예로 볼게요. 보통은 이름을 받아서 "○○님, 환영합니다"라고 하는데, 이름을 모를 때(값을 안 넘길 때)는 "게스트님"으로 인사하게 하고 싶어요. 이럴 때 매개변수 기본값을 쓰면 돼요.
// instagram-clone-frontend/js/main.js
function greet(name = "게스트") {
console.log(name + "님, 환영합니다");
}
greet("hong_tutor"); // hong_tutor님, 환영합니다
greet(); // 게스트님, 환영합니다
콘솔 출력은 이래요.
hong_tutor님, 환영합니다
게스트님, 환영합니다
매개변수 뒤에 = "게스트"라고 적어둔 게 기본값이에요. greet("hong_tutor")처럼 값을 넘기면 그 값("hong_tutor")이 name에 들어가고, greet()처럼 아무것도 안 넘기면 기본값인 "게스트"가 대신 들어와요.
기본값은 "값이 없을 때를 대비한 안전장치"예요. 음식점에서 "맵기 안 고르면 보통맛으로 드릴게요"라고 미리 정해두는 것과 같죠. 사용자가 깜빡하고 값을 안 넘겨도 함수가 빈 채로 어색하게 동작하지 않고, 미리 정해둔 기본값으로 자연스럽게 처리돼요.
🙋 학생 질문 — "튜터님, 기본값을 안 정해두면 값을 안 넘겼을 때 어떻게 되나요?"
지난 시간에 배운 undefined(아직 값이 안 들어온 상태)가 들어와요. greet에 기본값이 없었다면, greet()로 불렀을 때 name이 undefined가 되어 "undefined님, 환영합니다"라고 어색하게 찍혔을 거예요.
기본값(= "게스트")을 정해두면 이런 어색한 상황을 막을 수 있어요. "값을 안 넘기면 이걸로 채워줘"라고 미리 약속해두는 거죠. 그래서 깔끔한 함수를 만들 때 기본값을 자주 활용해요.
Step 5: rest parameter(...)
지금까지 만든 함수는 매개변수 개수가 딱 정해져 있었어요. announceLike(count)는 하나, greet(name)도 하나였죠. 그런데 받을 값의 개수가 몇 개일지 미리 모를 때가 있어요. 예를 들어 "여러 게시물에 한꺼번에 좋아요 누르기"는 한 개를 누를 수도, 세 개를 누를 수도 있잖아요.
이럴 때 매개변수 앞에 점 세 개(...)를 붙이면, 넘어온 값들을 하나의 묶음으로 모아서 받을 수 있어요. 이걸 rest parameter(나머지 매개변수)라고 불러요. 이름 그대로 "나머지를 다 모아라"는 뜻이에요.
// instagram-clone-frontend/js/main.js
function likeMultiplePosts(...postIds) {
console.log(postIds.length + "개 게시물에 좋아요를 눌렀어요");
for (const id of postIds) {
console.log("게시물 " + id + " 좋아요 완료");
}
}
likeMultiplePosts(1, 2, 3);
likeMultiplePosts(7);
콘솔 출력은 이래요.
3개 게시물에 좋아요를 눌렀어요
게시물 1 좋아요 완료
게시물 2 좋아요 완료
게시물 3 좋아요 완료
1개 게시물에 좋아요를 눌렀어요
게시물 7 좋아요 완료
...postIds는 호출할 때 넘긴 값들을 전부 postIds라는 하나의 묶음으로 모아요. likeMultiplePosts(1, 2, 3)을 부르면 postIds에 1, 2, 3이 묶여 들어오고, likeMultiplePosts(7)을 부르면 7 하나만 들어와요.
likeMultiplePosts(1, 2, 3)
│ │ │
└──┴──┴──▶ postIds = [1, 2, 3] ← 한 묶음으로!
likeMultiplePosts(7)
│
└────────▶ postIds = [7]
묶음으로 받았기 때문에 postIds.length로 개수(3개 / 1개)를 알 수 있고, 지난 시간에 배운 for...of로 하나씩 꺼내서 처리할 수 있어요. 개수가 몇 개든 함수 하나로 다 받아내는 거죠. 점 세 개(...) 덕분에 "좋아요 한 개 눌렀을 때"와 "세 개 눌렀을 때"를 따로 만들 필요가 없어요.
Step 6: 재사용 유틸 함수 모음 완성
자, 이제 오늘 배운 함수들을 한자리에 모아서 작은 유틸 함수 모음을 만들어봅시다. 유틸(util)은 "도구"라는 뜻이에요. 자주 쓰는 동작을 함수로 만들어두고 필요할 때마다 꺼내 쓰는, 나만의 연장통이라고 생각하면 돼요.
이번엔 좋아요를 켜고 끄는 토글 함수를 하나 추가하고, Step 3에서 만든 숫자 포맷 함수(formatLikeCount)와 함께 써볼게요.
// instagram-clone-frontend/js/main.js
function toggleLike(liked) {
return !liked; // 눌렀으면 취소, 안 눌렀으면 누름
}
let myLiked = false;
myLiked = toggleLike(myLiked); // true
console.log("좋아요 상태: " + myLiked + " / 표시: " + formatLikeCount(1241));
myLiked = toggleLike(myLiked); // false
console.log("좋아요 상태: " + myLiked);
콘솔 출력은 이래요.
좋아요 상태: true / 표시: 1.2천
좋아요 상태: false
한 줄씩 볼게요.
toggleLike(liked)— 좋아요 상태(liked)를 받아서 반대로 뒤집어 돌려줘요. 지난 시간에 배운 NOT 연산자(!) 기억나시죠?!liked는true면false로,false면true로 뒤집어요. 좋아요 버튼을 누를 때마다 켜졌다 꺼졌다 하는 동작이 딱 이거예요.myLiked = false— 처음엔 좋아요를 안 누른 상태예요.- 첫
toggleLike(myLiked)—false를 뒤집어true가 돼요(좋아요 누름!). 동시에formatLikeCount(1241)로 좋아요 수를 "1.2천"으로 예쁘게 표시하고요. 함수 두 개를 한 줄에서 같이 쓰는 게 보이시나요. - 두 번째
toggleLike(myLiked)—true를 다시 뒤집어false가 돼요(좋아요 취소!).
이렇게 함수로 묶어두니 main.js가 얼마나 깔끔해졌는지 보세요. "좋아요 켜고 끄기"는 toggleLike, "숫자 보기 좋게"는 formatLikeCount, "안내 문구"는 announceLike. 각 동작에 이름이 붙어 있어서, 나중에 코드를 다시 봐도 "아, 여기서 좋아요를 토글하는구나" 하고 한눈에 읽혀요.
게시물이 백 개로 늘어나도 이 함수들을 이름만 불러 쓰면 되죠. 지난 시간에 답답했던 "복사 붙여넣기" 문제가 완전히 해결된 거예요.
💡 함수에 이름을 잘 붙이면 코드가 곧 설명서가 돼요.
toggleLike(myLiked)라고 적혀 있으면 주석 없이도 무슨 일을 하는지 바로 알 수 있죠. 좋은 함수 이름은 "이 함수가 무엇을 하는가"를 동사로 또렷하게 보여줘요.
마무리
오늘 우리는 반복되는 코드를 함수로 묶어 재사용하는 법을 배웠어요. 지난 시간 답답했던 "복사 붙여넣기" 문제를 함수 하나로 깔끔하게 풀었죠. 짧게 되짚어볼게요.
- 함수 선언식 —
function 이름(매개변수) { ... }로 코드를 묶고 이름을 붙여요. 정의하고, 이름으로 호출해요. - 함수 표현식 · 화살표 함수 — 함수를 변수에 담을 수 있고(
const x = function(){}),=>로 더 짧게 줄일 수 있어요(const x = () => {}). 한 줄짜리는 중괄호·return도 생략 가능. - 매개변수와 반환값 — 매개변수로 입력을 받고
return으로 결과를 돌려줘요.console.log(찍고 끝)와return(건네줌)은 다른 일이에요. - 매개변수 기본값 —
name = "게스트"처럼 값을 안 넘겼을 때 쓸 기본값을 정해둬요. - rest parameter(
...) — 개수가 안 정해진 인자를 하나의 묶음으로 모아 받아요. - 유틸 함수 모음 —
toggleLike+formatLikeCount를 모아, 동작마다 이름 붙은 깔끔한 코드를 완성.
오전에 Java로 메서드를 배웠다면 오늘 함수가 낯설지 않았을 거예요. "코드를 묶어 이름 붙이고, 입력을 받아 결과를 돌려준다"는 건 언어가 달라도 똑같은 프로그래밍의 기본기거든요.
다음 시간 예고
오늘 함수를 만들면서 한 가지 궁금증이 생길 수 있어요. 함수 안에서 만든 변수는 함수 밖에서도 보일까요? formatLikeCount 안에서 쓴 count를 함수 밖에서 부르면 어떻게 될까요? 사실 함수 안의 변수는 함수 밖에선 안 보여요. 이렇게 변수가 보이는 범위를 스코프(scope)라고 불러요. 다음 시간에 이 스코프를 제대로 파헤칩니다.
거기서 한 발 더 나아가, 함수가 자기가 만든 변수를 계속 기억하는 신기한 능력인 클로저(closure), 그리고 함수를 다른 함수에 값처럼 넘겨주는 콜백(callback)까지 배워요. 함수를 "그냥 부르는 것"을 넘어 "다른 함수에 건네주는" 새로운 차원이 열리죠.
그리고 이걸 다 모아서, 게시물을 카테고리별로 걸러내는 **피드 필터링 함수(filterPosts)**를 직접 만들어볼 거예요. "여행 게시물만 보여줘", "사진 게시물만 골라줘" 같은 진짜 인스타그램스러운 기능이죠. 오늘 만든 함수가 다음 시간엔 훨씬 똑똑해질 거예요. 기대하세요!
과제
오늘 배운 함수를 직접 손에 익혀볼 차례예요. 기초 → 응용 → 탐구 순서로 풀어보세요. 모든 과제는 콘솔(console.log)에서 확인하고, 오늘 배운 함수·매개변수·return·기본값·rest parameter만으로 충분히 풀 수 있어요. 화면 조작은 아직 안 써도 돼요.
[구현] 환영 인사 함수 만들기 (기초)
js/main.js 맨 아래에 이어서, 사용자 이름을 받아 환영 인사를 콘솔에 찍는 함수를 만들어보세요.
function welcome(username) { ... }형태로 함수를 정의하세요.- 함수 안에서
console.log로"○○님, 인스타에 오신 걸 환영합니다!"를 찍으세요(○○자리에 매개변수username을 넣기). welcome("hong_tutor"),welcome("minji")처럼 이름을 바꿔가며 두 번 이상 호출해, 같은 함수가 이름만 바꿔 재사용되는 걸 콘솔에서 확인하세요.
[구현] 좋아요 수 합산 함수 (응용)
여러 게시물의 좋아요 수를 한꺼번에 받아 총합을 돌려주는 함수를 만들어보세요. 개수가 몇 개일지 모르니 rest parameter를 활용하는 게 핵심이에요.
function sumLikes(...counts) { ... }형태로, 점 세 개(...)로 좋아요 수들을 하나의 묶음으로 받으세요.- 함수 안에서
let total = 0;으로 합계 변수를 0에서 시작하고,for...of로 묶음을 하나씩 꺼내total에 더하세요. - 합계를
return total;로 돌려주세요(콘솔에 직접 찍지 말고return으로 돌려주는 게 포인트!). console.log(sumLikes(10, 20, 30));처럼 호출해서60이 나오는지,console.log(sumLikes(5));로5가 나오는지 확인하세요.
[탐구] 같은 함수를 세 가지 형태로 바꿔보기
오늘 같은 함수를 선언식·표현식·화살표 세 가지로 만들어봤죠. 직접 변환을 연습하면서, 한 줄 화살표가 어디까지 줄어드는지 관찰해보세요.
- 숫자를 받아 세 배로 돌려주는 함수를 먼저 함수 선언식으로 만드세요(
function triple(n) { return n * 3; }). - 같은 함수를 함수 표현식으로 바꿔보세요(
const triple = function (n) { ... };). - 다시 화살표 함수로, 그리고 마지막엔 중괄호와
return을 뺀 한 줄 화살표(const triple = (n) => n * 3;)까지 줄여보세요. - 세 형태 모두
console.log(triple(4));가12로 똑같이 나오는지 확인하고, "모양은 달라도 결과는 같다"를 자기 말로 한 문장 정리해보세요.
생각해볼 주제
정답을 적는 문제가 아니에요. 오늘 배운 것의 "왜"를 곱씹어보는 질문들이에요. 스스로 답을 만들어본 뒤, 예시답안과 비교해보세요.
1. 왜 같은 코드를 복사하는 대신 함수로 묶을까?
게시물마다 console.log를 복사해 붙여도 화면 결과는 똑같이 나와요. 그런데도 우리는 굳이 함수로 묶었죠. 만약 안내 문구를 "좋아요 ○개"에서 "❤️ ○명이 좋아합니다"로 바꿔야 한다면, 복사한 코드와 함수로 묶은 코드 중 어느 쪽이 고치기 쉬울까요? "한 곳만 고치면 전부 바뀐다"는 관점에서 함수의 진짜 이점을 생각해보세요.
2. console.log로 찍는 함수와 return으로 돌려주는 함수, 무엇이 더 쓸모 있을까?
formatLikeCount를 만들 때, 함수 안에서 바로 console.log로 찍어버릴 수도 있었어요. 그런데 우리는 return으로 값을 돌려주는 쪽을 골랐죠. 만약 그 "1.2천"이라는 결과를 화면에 띄우기도 하고, 다른 계산에도 써야 한다면 어느 쪽이 유리할까요? 함수가 결과를 "보여주고 끝내는 것"과 "건네주는 것"의 차이를 곱씹어보세요.
3. 화살표 함수가 더 짧은데, 왜 함수 선언식도 여전히 쓸까?
화살표 함수는 function이라는 글자도 빼고 한 줄로도 줄일 수 있어서 분명 더 짧아요. 그렇다면 함수 선언식은 이제 안 써도 되는 옛날 문법일까요? 짧은 게 항상 좋은 걸까요? 코드를 처음 읽는 동료가 "이게 함수구나"를 알아채는 속도, 그리고 함수가 길어질 때의 가독성이라는 관점에서, 두 형태가 각각 빛나는 자리를 생각해보세요.
✅ 예시 답안정답 보기
과제와 생각해볼 주제의 예시답안이에요. 정답이 하나만 있는 건 아니에요. 함수 이름이나 변수 이름은 취향대로 골라도 좋아요. 중요한 건 반복되는 코드를 함수로 묶고, 매개변수로 값을 받아, 필요한 결과는
return으로 제대로 돌려줬는가 예요.
🎯 [과제 1 예시답안] 환영 인사 함수 만들기
핵심 접근
이름 하나만 바뀌고 나머지는 똑같은 인사말을, 함수로 묶어 재사용하는 과제예요. 핵심은 바뀌는 부분(이름)을 매개변수로 빼는 거예요. username을 매개변수로 받아 두면, welcome("hong_tutor")처럼 호출할 때마다 그 자리에 다른 이름이 끼워져 같은 함수가 매번 다른 인사를 찍어요. 인사말 문장은 함수 안에 한 번만 적어 두면 돼요.
예시 구현
// instagram-clone-frontend/js/main.js 맨 아래에 이어서
function welcome(username) {
console.log(username + "님, 인스타에 오신 걸 환영합니다!");
}
welcome("hong_tutor"); // hong_tutor님, 인스타에 오신 걸 환영합니다!
welcome("minji"); // minji님, 인스타에 오신 걸 환영합니다!
welcome("hong_tutor")를 호출하면 username 자리에 "hong_tutor"가 들어가서 첫 줄이 찍히고, welcome("minji")를 호출하면 같은 함수가 이번엔 "minji"로 인사해요. 문장은 함수 안에 딱 한 번 적었는데, 호출만 바꿔서 두 번 재사용한 거예요.
hong_tutor님, 인스타에 오신 걸 환영합니다!
minji님, 인스타에 오신 걸 환영합니다!
채점 포인트
| 항목 | 확인 내용 |
|---|---|
| 함수 정의 | function welcome(username) { ... } 형태로 함수를 만들었는가 |
| 매개변수 사용 | 인사말 안에서 매개변수 username을 실제로 끼워 넣었는가 |
| 재사용 확인 | 이름을 바꿔가며 두 번 이상 호출해 콘솔에서 확인했는가 |
흔한 실수
- 이름을 함수 안에 고정해버림 —
console.log("hong_tutor님, ...")처럼 이름을 함수 안에 박아 두면, 매개변수가 무용지물이 돼요. 그러면minji를 인사하려고 함수를 또 만들어야 하죠. 바뀌는 부분은 반드시 매개변수로 받아야 같은 함수를 재사용할 수 있어요. - 호출을 한 번만 함 — 함수를 정의만 하고
welcome("...")호출을 빠뜨리면 아무것도 안 찍혀요. 함수는 "정의"와 "호출"이 따로예요. 정의는 요리법을 적어 둔 것이고, 호출해야 실제로 요리가 돼요.
🎯 [과제 2 예시답안] 좋아요 수 합산 함수
핵심 접근
개수가 몇 개일지 모르는 좋아요 수들을 한꺼번에 받아 총합을 돌려주는 과제예요. 핵심은 두 가지예요. 첫째, 점 세 개(...) rest parameter로 들어오는 값들을 하나의 묶음으로 받는 것. 둘째, 합계를 console.log로 찍지 말고 return으로 돌려주는 것. 합계 변수는 0에서 시작해 for...of로 묶음을 하나씩 꺼내며 더해요.
예시 구현
// instagram-clone-frontend/js/main.js 맨 아래에 이어서
function sumLikes(...counts) {
let total = 0;
for (const c of counts) {
total = total + c;
}
return total;
}
console.log(sumLikes(10, 20, 30)); // 60
console.log(sumLikes(5)); // 5
sumLikes(10, 20, 30)을 호출하면 ...counts가 10, 20, 30을 [10, 20, 30] 한 묶음으로 받아요. total을 0에서 시작해 for...of로 10, 20, 30을 차례로 꺼내 더하면 60이 되고, 그 값을 return으로 돌려줘요. sumLikes(5)는 묶음에 5 하나만 들어와 그대로 5가 나와요.
60
5
채점 포인트
| 항목 | 확인 내용 |
|---|---|
| rest parameter | ...counts로 개수가 정해지지 않은 값들을 묶음으로 받았는가 |
| 합계 누적 | total을 0에서 시작해 for...of로 하나씩 더했는가 |
| return 사용 | 합계를 console.log가 아니라 return total;로 돌려줬는가 |
흔한 실수
- 함수 안에서 바로
console.log로 찍음 — 합계를 함수 안에서 찍어버리면 그 값을 밖에서 다시 쓸 수 없어요. 과제의 핵심은return으로 값을 "건네주는" 거예요. 그래야console.log(sumLikes(...))처럼 돌려받은 값을 자유롭게 활용할 수 있어요. total을for...of안에서 0으로 초기화 —let total = 0;을 반복문 안에 넣으면 매번 0으로 되돌아가서 마지막 값만 남아요. 시작값은 반복문 바깥에서 한 번만 정해야 해요. (지난 시간 글자 수 세기에서도 만났던 함정이에요.)
🎯 [과제 3 예시답안] 같은 함수를 세 가지 형태로 바꿔보기
핵심 접근
"숫자를 세 배로 돌려준다"는 똑같은 일을, 선언식·표현식·화살표 세 가지 모양으로 적어 보는 과제예요. 핵심은 모양만 다를 뿐 하는 일은 똑같다는 걸 결과로 확인하는 거예요. 화살표 함수는 중괄호와 return까지 빼면 한 줄로 줄어드는데, 이렇게 줄여도 triple(4)가 셋 다 똑같이 12가 나와요.
예시 구현
// instagram-clone-frontend/js/main.js 맨 아래에 이어서
function triple(n) { return n * 3; } // 선언식
const tripleExpr = function (n) { return n * 3; }; // 표현식
const tripleArrow = (n) => n * 3; // 한 줄 화살표
console.log(triple(4)); // 12
console.log(tripleExpr(4)); // 12
console.log(tripleArrow(4)); // 12
선언식은 function 키워드로 이름을 직접 붙여 만들고, 표현식은 만든 함수를 변수(tripleExpr)에 담아요. 화살표는 function 글자를 빼고 =>로 적는데, 본문이 return 한 줄뿐이라 중괄호와 return까지 생략해 한 줄로 줄였어요. 세 형태 모두 4를 넣으면 12가 나와요. 모양은 셋 다 다른데 결과는 같아요.
12
12
12
한 문장 정리 예시: "함수는 적는 방식이 여러 가지지만, n을 받아 세 배로 돌려준다는 하는 일은 똑같아서 결과도 같다."
채점 포인트
| 항목 | 확인 내용 |
|---|---|
| 세 형태 모두 작성 | 선언식·표현식·화살표(한 줄 포함) 셋을 다 만들었는가 |
| 결과 동일 확인 | 셋 다 triple(4)가 12로 똑같이 나오는지 확인했는가 |
| 한 문장 정리 | "모양은 달라도 결과는 같다"를 자기 말로 정리했는가 |
흔한 실수
- 한 줄 화살표에
return을 그대로 남김 —(n) => return n * 3처럼 쓰면 에러가 나요. 중괄호를 뺀 한 줄 화살표는 그 줄의 결과를 자동으로 돌려주기 때문에return을 같이 쓰면 안 돼요.return을 쓰고 싶으면 중괄호를 살려(n) => { return n * 3; }로 적어야 해요. - 같은 이름을
const로 세 번 선언 — 셋 다const triple로 적으면 이름이 겹쳐 에러가 나요. 비교 실습이니triple,tripleExpr,tripleArrow처럼 이름을 달리해서 셋을 나란히 확인하는 게 좋아요.
💭 [생각해볼 주제 예시답안]
1. 왜 같은 코드를 복사하는 대신 함수로 묶을까?
문제 상황 요약
게시물마다 console.log를 복사해 붙여도 화면 결과는 똑같이 나와요. 그런데도 우리는 굳이 함수로 묶었죠. 만약 안내 문구를 "좋아요 ○개"에서 "❤️ ○명이 좋아합니다"로 바꿔야 한다면, 복사한 코드와 함수로 묶은 코드 중 어느 쪽이 고치기 쉬울까요?
튜터의 가이드 및 해설
복사와 함수의 차이는 "바꿀 때 몇 군데를 손대야 하는가" 에서 갈려요.
게시물 50개에 인사 문구를 복사해 붙였다고 해볼게요. 처음엔 둘 다 똑같이 잘 돌아가요. 문제는 문구를 고쳐야 할 때 터져요. "좋아요 ○개"를 "❤️ ○명이 좋아합니다"로 바꾼다면, 복사본은 50군데를 전부 찾아 고쳐야 해요. 하나라도 빠뜨리면 어떤 게시물은 옛 문구, 어떤 게시물은 새 문구가 섞여 나오죠. 사람이 50번 손으로 고치다 보면 빠뜨리는 건 시간 문제예요.
함수로 묶었다면 문구가 함수 안에 한 곳에만 있어요. 그 한 줄만 고치면 그 함수를 부르는 모든 자리에 자동으로 반영돼요. 누락도, 불일치도 생길 수 없어요. 이게 "같은 코드를 두 번 적지 말자"는 원칙이에요(흔히 DRY, Don't Repeat Yourself라고 불러요).
정리하면 이렇게 갈려요.
- 복사 붙여넣기: 처음 작성은 빠름. 하지만 바꿀 때마다 모든 복사본을 손대야 하고, 누락·불일치 위험이 커요.
- 함수로 묶기: 처음에 함수를 만드는 한 단계가 더 필요. 대신 고칠 땐 한 곳만 바꾸면 전부 반영돼요.
- 그래서 보통은: 같은 코드가 두 번 이상 반복되는 순간 함수로 묶어요. 코드는 한 번 쓰고 여러 번 고치는 법이라, 고치기 쉬운 쪽이 결국 이득이거든요.
🎯 면접관을 홀리는 핵심 멘트
"함수로 묶는 진짜 이유는 재사용보다 유지보수라고 생각해요. 같은 코드를 복사하면 문구 하나 바꿀 때 모든 복사본을 손대야 하고, 하나라도 빠뜨리면 화면이 제각각이 돼요. 함수로 묶으면 고칠 곳이 한 군데뿐이라, 바뀌는 부분을 한 곳에 모아 두는 게 버그를 줄이는 길이라고 봅니다."
2. console.log로 찍는 함수 vs return으로 돌려주는 함수
문제 상황 요약
formatLikeCount를 만들 때, 함수 안에서 바로 console.log로 찍어버릴 수도 있었어요. 그런데 우리는 return으로 값을 돌려주는 쪽을 골랐죠. 그 "1.2천"이라는 결과를 화면에 띄우기도 하고, 다른 계산에도 써야 한다면 어느 쪽이 유리할까요?
튜터의 가이드 및 해설
console.log로 찍는 함수와 return으로 돌려주는 함수의 차이는 "결과를 다시 쓸 수 있느냐" 예요.
console.log로 찍는 함수는 결과를 화면에 보여주고 거기서 끝나요. 보고 나면 그 값은 사라져요. 다른 데 쓰고 싶어도 손에 쥘 수가 없어요. 마치 누가 답을 소리 내어 읽어주고 종이는 안 주는 것과 같아요. 들은 그 순간엔 알지만, 그 값으로 다른 계산을 이어서 할 수가 없죠.
return으로 돌려주는 함수는 결과를 호출한 쪽에 건네줘요. 그러면 그 값을 받아서 원하는 대로 쓸 수 있어요. 화면에 띄우고 싶으면 console.log(formatLikeCount(1200))처럼 받아서 찍으면 되고, 다른 계산에 쓰고 싶으면 그 값을 변수에 담아 이어가면 돼요. 심지어 다른 함수의 입력으로 넘길 수도 있어요. 한 번 만든 결과를 여러 용도로 재활용할 수 있는 거예요.
두 방식을 이렇게 비교할 수 있어요.
console.log로 찍기: 만들기 간단하고, 값을 빠르게 눈으로 확인할 때 편해요. 하지만 결과를 재사용할 수 없어요(보여주고 끝).return으로 돌려주기: 결과를 받아서 화면 표시·추가 계산·다른 함수 입력 등 자유롭게 재활용할 수 있어요. 대신 받아서 쓰는 한 단계가 필요해요.- 그래서 보통은: 값을 만들어내는 함수(계산·변환)는
return으로 돌려줘요. 함수의 본질은 "값을 가공해 돌려주는 부품"이라, 돌려주면 어디에든 끼워 쓸 수 있거든요.console.log는 그렇게 돌려받은 값을 확인할 때 쓰는 거고요.
🎯 면접관을 홀리는 핵심 멘트
"함수 안에서 바로 찍어버리면 그 함수는 보여주는 것밖에 못 해요. 저는 값을 만들어내는 함수는
return으로 돌려주게 만들어요. 그래야 그 결과를 화면에 띄우든, 다른 계산에 넣든, 다른 함수의 입력으로 넘기든 마음대로 재활용할 수 있거든요. 함수의 본질은 값을 건네주는 부품이라고 생각합니다."
3. 화살표 함수가 더 짧은데, 왜 함수 선언식도 여전히 쓸까?
문제 상황 요약
화살표 함수는 function이라는 글자도 빼고 한 줄로도 줄일 수 있어서 분명 더 짧아요. 그렇다면 함수 선언식은 이제 안 써도 되는 옛날 문법일까요? 짧은 게 항상 좋은 걸까요?
튜터의 가이드 및 해설
결론부터 말하면, 짧다는 게 항상 좋은 건 아니에요. 둘은 각자 빛나는 자리가 달라요.
먼저 함수 선언식의 장점이에요. 선언식은 function welcome(...)처럼 function이라는 글자와 이름이 또렷하게 드러나요. 코드를 처음 읽는 동료가 "아, 이건 welcome이라는 함수구나"를 한눈에 알아채요. 또 선언식은 적어 둔 위치보다 위에서 호출해도 동작해요(이걸 호이스팅이라고 해요). 그래서 길고 여러 곳에서 불리는 핵심 함수일수록, 이름이 드러나고 어디서든 부를 수 있는 선언식이 읽기에 편해요.
화살표 함수의 장점은 짧음이에요. (n) => n * 3처럼 한 줄로 끝나는 짧은 함수, 특히 "이 자리에서 잠깐 한 번 쓰고 마는" 일회용 함수에 잘 어울려요. 군더더기 없이 핵심만 남기니까요. (앞으로 어떤 기능에 "잠깐 실행할 짧은 함수"를 넘겨주는 경우가 자주 나오는데, 그럴 때 화살표가 특히 깔끔해요. 그건 다음 시간에 자세히 다뤄요.)
그래서 선택은 이렇게 갈려요.
- 함수 선언식: 이름이 또렷이 드러나 가독성이 좋고, 위에서 호출해도 됨(호이스팅). 길고 여러 곳에서 쓰는 핵심 함수에 적합.
- 화살표 함수: 짧고 군더더기가 없음. 한 줄짜리 짧은 함수, 일회용 함수에 적합.
- 그래서 보통은: 짧음만 보고 무조건 화살표로 통일하지 않아요. "이 함수가 길고 여러 곳에서 불리는가, 아니면 짧고 한 번 쓰고 마는가"를 기준으로 골라요. 둘은 경쟁 관계가 아니라 쓰임이 다른 도구예요.
🎯 면접관을 홀리는 핵심 멘트
"화살표가 짧다고 모든 함수를 화살표로 통일하진 않아요. 선언식은 이름과
function이 또렷해서 읽는 사람이 함수임을 바로 알아채고, 위에서 호출해도 동작하거든요. 그래서 길고 여러 곳에서 쓰는 핵심 함수는 선언식, 짧고 한 번 쓰고 마는 함수는 화살표로 골라 써요. 짧음이 항상 가독성은 아니라고 생각합니다."