brunch

You can make anything
by writing

C.S.Lewis

by 김민태 Jan 13. 2016

프로그래밍 — 단순한 기능, 기능의 결합, 의미의 부여

어떤 언어든  상관없지만 가장 손쉽게 접할 수 있는 언어인 Javascript 문법을 사용합니다.


쉬운 설명을 위해 사칙연산을 수행하는 함수를 하나씩 만들어볼게요.


function add(a, b) {

  return a + b;

}


function sub(a, b) {

  return a - b;

}


function multip(a, b) {

  return a * b;

}


function div(a, b) {

  return a / b;

}


두 수를 입력으로 받고 덧셈(add), 뺄셈(sub), 곱셈(multip), 나눗셈(div)을 하여 그 결과를 반환(return)하는 아주 간단한 함수들을 만들었어요. 한번 사용해볼까요?

3과 4를 더하고 싶다면?


add(3, 4);// 결과는 7


3에서 4를 빼고 싶다면?


sub(3, 4);// 결과는 -1 이되겠죠?


이렇게 너무나 간단한 일만 하는 함수(기능)들은 서로 연관성이 전혀 없고 그 자체로는 어떠한 유연성도 제공하지 못합니다.  3+10+100을 add 함수를 사용해서 수행할 수 있을까요? add 함수는 입력으로 두 개의 수 만을 받습니다. 3+10+100은 세 개나 되네요? 이런, 멍청한 함수 덕분에 저런 간단한 작업도 할 수 없네요. 어떻게 하면 될까요?


add 함수가 세 개의 입력을 받도록 수정해봅시다.


function add(a, b, c) {

    return a + b + c;

}


자, 이제 3+10+100을 add 함수로 해볼까요?


add(3, 10, 100);// 결과는 당연히 113이 나오겠지요?


그럼 다시 3+4를 해볼까요?


add(3, 4, 0);


두 개의 값 만을 더하려 하니 세 개의 입력을 받도록 수정된 add 함수의 세 번째 인수에 0을 넣는 트릭을 써야만 하네요. 영 마음에 들지 않습니다. 그렇죠?


이번엔  3+10+100+200+300을 해볼까요? 이번엔 3개가 아니라 다섯 개의 숫자를 더해야 합니다. add 함수를 다시 수정해야 할까요? 그런 방법도 있겠지만 적은 수의 합을 구할 때는 안 쓰는 자릿수에 0을 채워 넣는 지저분한 문제가 더 크게 부각되겠죠?


이렇게 특정한 요구사항에 정확히 일치하는 기능은 작은 요구사항 변경에도 대응하기 힘든 문제점을 가지고 있습니다. 무수히 많은 기능을 만들거나 다른 방법을 찾거나 해야 합니다.


새로운 방법으로 이 문제를 해결해 봅시다. 문제 해결의 실마리는 바로 기능의 “조합”입니다.


add 함수는  원래대로 두 개의 값을 더하는 기능으로 변경해 놓도록 하겠습니다. 그리고  add와  add를 조합하여 원하는 결과를 얻어보는 것이지요.


3+10+100을 해볼까요?  add는 값을 두개만 받지요?


add(3, 10);


100을 더하지 못했습니다. 어떻게 할까요?


add( add(3, 10), 100 );


코드를 잘 보세요. 바깥쪽 add의 인수는 두개입니다. 첫 번째 인수는 add(3, 10)이고 두 번째 인수는  100입니다. 첫 번째 인수인 add 함수의 결과가 먼저 실행되어 결과 13을 반환합니다. 반환된 13을 바깥쪽 add 함수의 첫 번째 인수로 사용하는 것이지요.


이건 사실 이렇게 표현한 것과 동일합니다.


add(3+10, 100);


다만 사칙 연산자가 아니라 함수라는 것으로 표현한  것뿐이죠.


이 간단한 규칙. 두 수를 합하여 결과를 반환하는 함수를 조합할 수 있다는 것 만으로 모든 가변 인수의 처리가 가능합니다. add 함수의 수정 없이 말이죠.


3+10+100+200+300을 해볼까요?


add( add( add( add(3, 10), 100 ), 200), 300 );


좀 길어 보일 뿐이지 같은 원리입니다.


이제 좀 더 문제를 확장하여  3+10*100–5를 해봅시다.


덧셈뿐만 아니라 곱셈과 뺄셈까지 조합되어있네요? 우리에겐 같은 방식으로 만들어 놓은 각각의 함수들이 있습니다. (맨 위 참조) 조합할 수 있다는 원칙을 그대로 적용해 문제를 풀어봅시다.


sub( multip( add(3, 10), 100), 5);


이렇게 되겠지요? 코드를 찬찬히 잘 살펴보시면 같은 방식이라는 걸 아실 수 있습니다. 재미있는 건 덧셈 함수와 뺄셈 함수, 곱셈 함수들은 서로 전혀 연관성이 존재하지 않습니다.


이를 의존성이 없다고 얘기하기도 해요. 서로 완전히 독립적이지요. 이렇게 의존성 없는 독립된 녀석들을 조합(결합)하여 새로운 일을 해결하는 방식을 “직교성”이라고 합니다.


직교성을 이용하면 굉장히 유연하게 원하는 일들을 할 수 있어요. 어떤 요구사항에 맞춘 새로운 기능을 만드는 것이 아닌, 독립적으로 작동하지만 기능들을 조합할 수 있다면 원하는 목적을 달성할 수 있는 것이죠.


10+10*100/2, 100*100/2 … 뭐든 사칙연산은 다 처리가 가능하게 되었습니다. 즉, 요구 사항이 계속 바뀐다 해도 add, sub, multip, div 함수는 변경하지 않아도 되는 것입니다. 규칙은 연산을 위한 최소한의 입력만을 받고, 그 결과를 돌려준다. 그리고 그 이상의 확장이 필요하면 조합하여 문제를 해결한다로 정리할 수 있겠습니다.


지금 까지의 내용으로 우리가 알 수 있는 사실 한 가지는 함수에 기능이  추가될수록 즉, 하는 일이  많아질수록 아이러니하게도 유연성은 떨어지게 되어있습니다. 함수는 가능하다면 한 가지 일만 하도록 만들어야 하는 이유가 여기에 있습니다.


자, 그런데 문제가 하나 있습니다. 단순한 원리로 동작된다 해도 시각적으로 복잡해 보이면 어려워하는 경향이 있습니다.


add( add( add( add(3, 10), 100 ), 200), 300 );


이 코드가 원리는 단순하지만 굉장히 난해하고 복잡해 보이고 그렇지요? 눈에도 잘 들어오지 않습니다. 이건 학습자의 수준 문제가 아닙니다. 실제로 단순하지만 복잡해 보이는 형태. 어떻게 해야 할까요?


작은 기능들을 조합하여 조금 더 복잡해 보이는 기능을 만드는 방식을 사용해봅시다. 여기서 중요한 것은 조금 더 인간다움을 더한다는 것입니다.


function double(a) {

  return add(a, a);

}


입력값 하나를 받고 그 값의 두배가 되는 값을 반환하는 함수  double을 만들었습니다. 10 의 두배 값을 얻으려면 다음과 같이 사용하면 되겠죠?


double(10);


double함수 내부는 기존에 만들어놓은 add 함수를 그대로 사용합니다. 물론 add를 사용하여 똑같이 해도 되겠지요?


add(10, 10);


두 개를 비교해 보세요. 어떤 방식이 10의 두 배 값을 구한다는 의미를 좀 더 잘 전달하나요?


입력값의 1/4 값을 반환하는 함수를 만들어볼까요?


function quater(a) {

  div(a, 4);

}


12달의 1분기 값 즉, 1 쿼터를 구해볼까요?


quarter(12);// 3이 되겠지요?


나눗셈 함수를 사용해볼까요?


div(12, 4);


어떤가요? 결과는 같지만 의미 관점에선 quarter 함수가 훨씬 이해하기 쉽지요?


이렇게 조금 더 의미를 더하여 이해할 수 있는 방식으로  단순화시키는 것을 “추상화”라고 합니다. 추상화의 단계도 같은 방식으로 여러 겹을 만들어 낼 수 있습니다.  div를 감싸서 quarter 란 함수로  추상화한  것처럼 말이죠. quarter 함수를 감싸서 더 높은 수준의 추상화된 함수를 만들 수 있습니다.


매거진의 이전글 기술이 세상을 이롭게 변화시킨다
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari