절차지향 vs 객체지향 프로그래밍

src/content/documents/dev-tools/procedural-vs-object-oriented-programming.json

한 줄 정의

절차지향은 “무엇을 어떤 순서로 할까?”를 중심으로, 객체지향은 “누가 어떤 책임을 맡을까?”를 중심으로 프로그램을 구성하는 방식이다.

둘은 특정 언어의 이름이 아니라 프로그램을 바라보는 패러다임(사고방식)이다. C++과 Python도 절차지향으로 작성할 수 있고, C에서도 구조체와 함수 포인터를 이용해 객체지향의 일부 아이디어를 구현할 수 있다.

먼저 비교하기

구분

절차지향 프로그래밍

객체지향 프로그래밍

출발점

처리 순서와 함수

객체의 역할과 책임

중심 질문

“다음에는 무엇을 하지?”

“누가 이 일을 해야 하지?”

기본 구성

데이터와 데이터를 처리하는 함수

상태와 행동을 묶은 객체

변경의 영향

공용 데이터·함수를 쓰는 여러 코드로 퍼질 수 있음

책임을 잘 나누면 관련 객체 안으로 제한됨

잘 맞는 상황

짧은 스크립트, 순서가 뚜렷한 작업, 알고리즘

역할이 많고 변경·확장이 잦은 시스템

대표적인 언어·사용 방식

C, 셸 스크립트, 단순 Python 스크립트

Java, C++, C#, Kotlin의 클래스 기반 설계

절차지향이 낡고 객체지향이 항상 우월한 것은 아니다. 작은 계산 프로그램에 클래스 계층을 많이 만들면 오히려 읽기 어렵다. 반대로 수많은 기능과 상태를 한 절차에서 관리하면 변경할 때 예상하지 못한 곳이 깨지기 쉽다.

예시: RPG 전투

절차지향

하나의 함수가 모든 캐릭터 종류를 알면 새 캐릭터가 추가될 때마다 새로운 변수들이 추가된다.

public class Main {
    public static void main(String[] args) {
        int playerHp = 100;
        int playerAtk = 30;
        //방어력, 치명타, 마력 추가하려면 변수를 더 만들어야함.
        int enemy1Hp = 80;
        int enemy1Atk = 20;
        //방어력, 치명타, 마력 추가하려면 변수를 더 만들어야함.

        System.out.println("=====초기 체력=====");
        System.out.println("플레이어 HP: " + playerHp);
        System.out.println("적 HP: " + enemyHp);

        enemyHp -= playerAtk; // 적 체력에 플레이어 공격력만큼 피해 ( 80 - 30 = 50 )
        playerHp -= enemyAtk; // 플레이어체력 적 공격력만큼 피해 ( 100 - 20 = 80 )

        System.out.println("=====공격후 체력=====");
        System.out.println("플레이어 HP: " + playerHp);
        System.out.println("적 HP: " + enemyHp);
    }
}
=====초기체력======
플레이어 HP: 100
적 HP: 80
=====공격후 체력=====
플레이어 HP: 80
적 HP: 50

객체 지향

하나의 함수가 모든 캐릭터 종류를 알면 새 캐릭터가 추가될 때마다 새로운 변수들이 추가된다.

class Character {
    int hp;
    int atk;
    //int def; //나중에 방어력, 마력, 치명타등 마음대로 추가 하면 모두 생김.

    Character(int hp, int atk) {
        this.hp = hp;
        this.atk = atk;
    }

    void attack(Character target) {
        target.hp -= atk;
    }
}

public class Main {
    public static void main(String[] args) {
        Character player = new Character(100, 30);
        Character enemy = new Character(80, 20);
        //Character enemy2 = new Character(50, 30);//간편하게 새로운적 추가.

        System.out.println("=====초기 체력=====");
        System.out.println("플레이어 HP: " + playerHp);
        System.out.println("적 HP: " + enemyHp);

        player.attack(enemy);
        enemy.attack(player);

        System.out.println("=====공격후 체력=====");
        System.out.println("플레이어 HP: " + playerHp);
        System.out.println("적 HP: " + enemyHp);
    }
}
=====초기체력======
플레이어 HP: 100
적 HP: 80
=====공격후 체력=====
플레이어 HP: 80
적 HP: 50

왜 객체지향을 사용하는가

게임, 쇼핑몰, 은행, SNS처럼 규모가 큰 시스템에는 플레이어, 주문, 계좌처럼 각자 다른 상태와 규칙을 가진 개념이 많다. 책임을 객체별로 나누면 다음과 같은 이점이 있다.

  • 관련 데이터와 동작을 가까이 둘 수 있다.

  • 내부 구현을 바꿔도 공개 인터페이스가 같으면 사용하는 코드는 덜 영향을 받는다.

  • 같은 인터페이스를 구현한 객체를 교체하기 쉽다.

  • 업무 용어와 코드의 이름을 비슷하게 맞춰 팀이 의사소통하기 쉽다.

하지만 책임을 잘못 나누거나 모든 것을 클래스로 만들면 파일과 간접 호출만 늘어난다. 객체지향의 장점은 클래스의 개수가 아니라 변경 이유가 같은 코드끼리 모으고, 다른 책임은 분리했는지에서 나온다.

무엇을 선택해야 할까

절차지향이 잘 맞는 경우

  • 입력을 읽고 정해진 단계로 변환한 뒤 출력하는 짧은 작업

  • 상태와 역할이 거의 없는 알고리즘

  • 한 번 실행하고 끝나는 자동화 스크립트

  • 처리 순서를 한눈에 보는 것이 중요한 데이터 파이프라인

객체지향이 잘 맞는 경우

  • 여러 대상이 각자의 상태와 규칙을 가질 때

  • 같은 동작을 여러 방식으로 교체해야 할 때

  • 기능 추가와 정책 변경이 자주 일어날 때

  • 팀이 업무 개념을 기준으로 역할을 나눠 개발할 때

실무에서는 한 가지만 고집하지 않는다. 객체로 전체 책임을 나누되, 객체 내부의 짧은 계산은 절차적으로 작성할 수 있다. Java의 Stream API처럼 상태 변경을 줄이고 데이터를 변환하는 함수형 프로그래밍도 객체지향과 함께 사용한다.

정리

기억할 문장은 두 개면 충분하다.

  • 절차지향: 무엇을 어떤 순서로 할까?

  • 객체지향: 누가 이 책임을 맡고 누구와 협력할까?

좋은 설계는 패러다임의 이름보다 문제의 크기, 변경 가능성, 팀이 이해하기 쉬운 구조를 기준으로 선택한다.

댓글 0

댓글을 불러오는 중…