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

Kim, min tae
ibare story
6 min readSep 1, 2014

--

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

두 수를 입력으로 받고 덧셈(add), 뺄셈(sub), 곱셈(multi), 나눗셈(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( multi( add(3, 10), 100), 5);

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

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

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

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

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

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

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

이 코드가 원리는 단순하지만 굉장히 난해해 보이고 복잡해 보이고 그렇지요? 눈에도 잘 들어오지 않습니다. 이건 학습자의 수준 문제가 아닙니다. 그저 인간의 특성입니다.

어떻게 해야할까요?

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

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

double(10);

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

add(10, 10);

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

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

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

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

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

div(12, 4);

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

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

매우 단순하지만 이렇게 단순한 방식을 계속 발전시켜 나아가는 것. 그것이 프로그래밍이라는 것입니다. 이런 방식이 계속 발전되어 객체지향이라는 것도 나오고, 디자인 패턴이라는 것도 나오게됩니다.

--

--