7 min read

컴퓨터 사고의 첫걸음, 절차지향 프로그래밍

컴퓨터 사고의 첫걸음, 절차지향 프로그래밍
Photo by Rick Muigo / Unsplash

몇 해 전 자녀에게 피넛버터를 바른 샌드위치를 만드는 방법을 알려달라는 영상이 유튜브에 개시가 되었습니다. 아빠는 설명서에 적힌 절차대로 진행했지만 우스꽝스러운 상황이 진행이 되죠.😅

저는 프로그래밍이 이와 상당히 유사한 점이 많다고 생각됩니다. 절차지향 프로그래밍이 바로 그렇습니다.


절차지향 프로그래밍이란?

절차지향 프로그래밍은 비교적 오래된 개념이지만, 오늘날에도 여러 시스템의 근간을 이루는 중요한 패러다임입니다. 이 방식은 “무엇을(What) 할 것인가”보다 “어떻게(How) 할 것인가”에 초점을 두며, 프로그램을 순서대로 실행되는 명령들의 흐름(절차)으로 바라봅니다. 그리고 이 흐름을 잘게 쪼개 함수(또는 서브루틴)로 구성해 전체 프로그램을 완성합니다.


절차지향 프로그래밍이 갖는 의미

1. 컴퓨터 사고의 출발점

처음 프로그래밍을 배울 때 절차지향적인 사고를 자연스럽게 접하게 됩니다.
예를 들면 다음과 같은 흐름입니다.

  1. 문제를 문장으로 정리한다.
  2. 해결 순서를 자연어로 작성한다.
  3. 해당 순서를 코드로 옮긴다.

이 접근 방식 자체가 절차지향의 핵심입니다. 알고리즘을 ‘단계들의 나열’로 이해할 수 있게 해주기 때문에, 프로그래밍 기초 교육에서도 매우 효과적인 방식입니다.


2. “흐름을 제어한다”는 관점

절차지향 프로그래밍은 프로그램을 제어 흐름(Control Flow) 중심의 시스템으로 봅니다.

  • if, for, while, switch 같은 제어문은
    “다음에 어떤 절차를 실행할지”를 결정하는 도구입니다.
  • 함수 호출은
    “다음에 어떤 묶음 작업을 수행할지”를 지정하는 행위입니다.

즉, 프로그램 전체를 하나의 흐름으로 바라보고, 그 흐름을 어떻게 제어할지에 초점을 맞추는 것이 절차지향의 본질입니다.
이 사고방식은 이후 객체지향(OOP)이나 함수형 프로그래밍을 학습할 때에도 자연스럽게 기반으로 남아, 다른 패러다임을 이해하는 데에도 도움을 줍니다.


예제를 기반으로 절차지향적으로 생각해 봅시다.


HTML 요소를 이용해 숫자 시계를 표현하는 프로그램을 절차지향 방식으로 이해해 봅시다.

code는 우하단 editor 클릭 -> 좌상단 햄버거 버튼 -> script.js를 보시면 확인할 수 있습니다.


1. 데이터 정의 단계 – 숫자를 구성하는 도형 정보

const angles = {
  "⎡": [0, 90],
  "⎤": [90, 180],
  "⎦": [180, 270],
  "⎣": [0, 270],
  "-": [0, 180],
  "|": [90, 270],
  " ": [135, 135],
};

여기서 각각의 기호는 시계 모양 안에서 바늘 두 개를 어떤 각도로 배치할지를 의미합니다. 수학적 모델이나 객체 간 관계를 먼저 세우는 방식이 아니라,
“디자인을 구성하는 요소 = 바늘 두 개의 각도”
이런 단순한 데이터 구조를 먼저 만듭니다.

다음, 각 숫자를 표현하기 위한 24개의 셀(cell) 패턴을 정의합니다.

const numbers = {
  0: ["⎡","-","-", ... ],
  1: ["⎡","-","⎤", ... ],
  ...
};

각 숫자는 24칸의 작은 시계로 구성되며, 각 칸을 어떤 기호로 표시할지 배열로 담아둡니다. 이 구조는 데이터를 먼저 나열하고, 나중에 절차를 통해 화면에 반영하는 방식입니다. 절차지향 에서는 이런 “데이터 우선 정의 → 처리 단계에서 활용” 흐름이 매우 자연스럽습니다.

2. 화면 구성 단계 – 숫자를 그릴 시계 구조 생성

이제 프로그램은 숫자를 표현할 수 있는 시계 셀 24개를 한 번에 만들어야 합니다.
이 역할을 하는 것이 createDigit() 함수입니다.

const createDigit = () => {
  const digit = document.createElement("div");
  const clock = document.createElement("div");
  const hand1 = document.createElement("div");
  const hand2 = document.createElement("div");

  digit.classList.add("digit");
  clock.classList.add("clock");
  hand1.classList.add("hand");
  hand2.classList.add("hand");

  clock.appendChild(hand1);
  clock.appendChild(hand2);

  for (let i = 0; i < 24; i++) {
    digit.appendChild(clock.cloneNode(true));
  }
  wrapper.appendChild(digit);
};

“셀 하나 만들고 → 그걸 24번 복제해 붙인다 → 완성된 한 개의 숫자를 화면에 추가한다” 라는 명령형 절차의 집합입니다.

시계 숫자는 총 6자리(시·시·분·분·초·초)이므로,

for (let i = 0; i < 6; i++) {
  createDigit();
}

“숫자 하나 만들기”라는 절차를 6번 반복 수행합니다.

3. 갱신 절차 – 특정 숫자를 화면에 표시하는 과정

디지털 시계는 1초마다 숫자가 바뀌기 때문에, 특정 숫자를 구성한 24개의 셀 각각을 새 패턴으로 바꿔줘야 합니다.
이 일을 담당하는 것이 updateDigit() 함수입니다.

function updateDigit(digit, number) {
  for (const cell of digit.children) {
    let index = Array.from(digit.children).indexOf(cell);
    cell.children[0].style.rotate = angles[numbers[number][index]][0] + "deg";
    cell.children[1].style.rotate = angles[numbers[number][index]][1] + "deg";
  }
}

여기서 핵심은 두 가지입니다.

  1. 셀의 위치(index)에 따라 어떤 기호를 넣을지 결정
  2. 기호에 매핑된 두 개의 각도 값을 바늘에 그대로 적용

절차지향적으로 보면, 이 함수는 특별한 구조 없이:

  • 배열에서 값 꺼내기
  • 대응되는 데이터 조회하기
  • 스타일 반영하기

이 세 절차를 순서대로 수행합니다.

4. 전체 흐름 제어 – 1초마다 시간 가져와 갱신

시계 프로그램의 마지막 단계는
정해진 시간마다 특정 절차를 반복 실행하는 것입니다.

const alldigits = document.querySelectorAll(".digit");
  
function startClock () {
  setInterval(() => {
  new Date()
    .toLocaleTimeString("ko-KR", {
      hour12: true,
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
    })
    .replace(/\D/g, "")
    .split("")
    .map((num, i) => {
      updateDigit(alldigits[i], num);
    });
  }, 1000);
}
  
document.addEventListener('DOMcontentLoaded', () => {
  startClock();
})

순서대로 보면:

  1. 현재 시간을 문자열로 가져온다
  2. 숫자가 아닌 문자를 제거한다
  3. 각각의 자리수로 분리한다
  4. 각 자리수를 화면의 해당 digit에 반영한다

이 부분은 절차지향의 관점에서 가장 분명합니다.
이 코드의 목적은 단순합니다

“1초마다 현재 시간을 기반으로 모든 숫자 셀을 업데이트한다.”

어떤 객체가 어떤 책임을 갖는다든지 하는 관점보다는,
시간 가져오기 → 숫자 파싱 → 화면 갱신
이라는 절차의 흐름이 핵심이죠.

데이터 정의 -> 구성 요소 생성 -> 갱신 로직 정의 -> 주기적 실행의 흐름이 보이시나요?


마무리

절차지향 프로그래밍은 가장 기본적이면서 파워풀한 프로그래밍 방식입니다. “무엇을 먼저 하고, 그 다음 무엇을 해야 하는지” 의 흐름을 중심으로 프로그램을 구성합니다.

복잡한 구조를 추상화하지 않아도,
“시간에 따라 화면을 바꿔라”는 문제는 절차만 잘 짜면 충분히 해결됩니다.
이 점이 절차지향 방식의 장점이기도 합니다.