명령형 프로그래밍과 선언형 프로그래밍

MyeonghoonNam·2021년 8월 17일
0

이 포스팅을 읽기 전에 프로그래밍 패러다임에 대해 먼저 이해하자.


명령형 프로그래밍

컴퓨터 과학에서 명령형 프로그래밍은 선언형 프로그래밍과 반대되는 개념입니다. 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종입니다.

자연 언어에서의 명령법이 어떤 동작을 할 것인지를 명령으로 표현하듯이, 명령형 프로그램은 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것입니다.

또한 문제에 관하여 어떻게 구현하는가를 디테일하게 기술하는 것에 관점을 두고 있습니다.

// 명령형 프로그래밍
const imperativeDouble = (arr) => {
  let results = [];

  for (let i = 0; i < arr.length; i++) {
    if (typeof arr[i] === 'number') {
      results.push(arr[i] * 2);
    }
  }

  return results;
};

document.querySelector('.box1').innerText = imperativeDouble([
  1,
  2,
  ,
  '',
  null,
  3,
  'undefiend',
]);

선언형 프로그래밍

프로그램이 실행하는 알고리즘에 대하여 명시하지 않고 목표를 명시하며 어떤 방법으로 목표를 어떻게 해결해야 하는지에 대해 집중하고 해결 방법은 컴퓨터에게 위임하는 프로그래밍 패러다임의 일종입니다.

예를 들어 대표적인 선언형 프로그래밍 방식인 HTML의 경우 화면에 무엇이 나타나야 하는지, 즉 무엇을 원하는지에 대해 묘사하는 것이지 어떤 방법으로 컴퓨터 화면에 페이지를 나타내야 하는지를 묘사하는 것이 아니기 때문입니다.

즉, 명령형 프로그램은 알고리즘을 명시하고 묙표는 명시하지 않는 데에 반해, 선언형 프로그램은 목표를 명시하고 알고리즘을 명시하지 않습니다.

// 선언형 프로그래밍
const declarativeDouble = (arr) => {
  return arr.filter((el) => typeof el === 'number').map((el) => el * 2);
};

document.querySelector('.box2').innerText = declarativeDouble([
  1,
  2,
  ,
  '',
  null,
  3,
  'undefiend',
]);

비교

명령형 프로그래밍과 선언형 프로그래밍을 코드로 구현하여 보며 비교해보자.

// 명령형 방식 토글 버튼 만들기

const button1 = document.createElement('button');
button1.innerText = 'Button 1';
let button1ClickCount = 0;

const button2 = document.createElement('button');
button2.innerText = 'Button 2';
let button2ClickCount = 0;

const button3 = document.createElement('button');
button3.innerText = 'Button 3';
let button3ClickCount = 0;

const toggleButton = (button) => {
  if (button.style.textDecoration === '') {
    // 버튼 1만 구현 2, 3 역시 중복 코드를 추가해야하기 때문
    button.style.textDecoration = 'line-through';
    button1ClickCount++;
    button.innerText = `Button 1 Count : ${button1ClickCount}`;
  } else {
    button.style.textDecoration = '';
  }
};

const main = document.querySelector('body');

main.appendChild(button1);
main.appendChild(button2);
main.appendChild(button3);

main.addEventListener('click', (e) => {
  toggleButton(e.target);
});

명령형 방식의 경우 명령 순서에 의존하다 보니 한 번에 가시적으로 기능을 파악하기도 힘들고 중복 코드가 많이 발생하며 예상치 못한 오류 발생 확률이 증가하게 된다.

// 선언형 방식 토글 버튼 만들기

// 명령형 방식 토글 버튼 만들기
// 기능 1 : 클릭할 때 삭선 생성
// 기능 2 : 1번 버튼만 3번 클릭할 때 마다 alert
// 기능 3 : 기본 3초 뒤에 자동 토글이 풀리게 되는 TimerButton
// 기능 4 : ButtonGroup 만들기

// 선언형 프로그래밍을 통한 추상화

function timerButton({ target, text, timer = 3000 }) {
  const button = new toggleButton({
    target,
    text,
    onClick: () => {
      setTimeout(() => {
        button.setState({
          ...button.state,
          toggled: !button.state.toggled,
        });
      }, timer);
    },
  });
}

function toggleButton({ target, text, onClick }) {
  const button = document.createElement('button');

  target.appendChild(button);

  this.state = {
    clickCount: 0,
    toggled: false,
  };

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.render = () => {
    button.innerText = text;

    button.style.textDecoration = this.state.toggled ? 'line-through' : 'none';
  };

  button.addEventListener('click', () => {
    this.setState({
      clickCount: this.state.clickCount + 1,
      toggled: !this.state.toggled,
    });

    if (onClick) {
      onClick(this.state.clickCount);
    }
  });

  this.render();
}

function buttonGroup({ target, buttons }) {
  const group = document.createElement('div');
  let isInit = false;

  this.render = () => {
    if (!isInit) {
      buttons.forEach(({ type, ...props }) => {
        if (type === 'toggle') {
          new toggleButton({ target: group, ...props });
        } else if (type === 'timer') {
          new timerButton({ target: group, ...props });
        }
      });

      target.appendChild(group);
      isInit = true;
    }
  };

  this.render();
}

const main = document.querySelector('body');

new buttonGroup({
  target: main,
  buttons: [
    {
      type: 'toggle',
      text: '토글 버튼 1',
    },
    {
      type: 'toggle',
      text: '토글 버튼 2',
    },
    {
      type: 'timer',
      text: '타이머 !',
      timer: 3000,
    },
  ],
});

명령형 방식보다 선언형 방식이 추상화를 통해 새로운 기능의 확장과 표현에 굉장히 용이하며 각각의 다양한 기능의 객체들을 다룰 수 있다.

위의 코드에서 추상화를 통한 상태를 기반으로 UI의 기능을 구현하므로 좀 더 선언적이며 DOM 접근의 복잡도를 많이 개선하게 되었다.

즉, 외부의 개입을 최소한으로 하여 독립된 상태를 기반으로 추상화 된 UI를 구현할 수 있다. 위와 같은 컴포넌트 단위의 추상화 과정을 잘 다루면 다양한 프레임워크들(리엑트, 뷰 등)에 대한 이해도를 올릴 수 있다.



참고자료

profile
꾸준히 성장하는 개발자를 목표로 합니다.

0개의 댓글