'함수형 프로그래밍'에 해당되는 글 1건

  1. 2025.04.05 코드의 세계를 지배하는 4가지 기둥, 객체지향 프로그래밍의 모든 것
반응형

코드의 세계를 지배하는 4가지 기둥, 객체지향 프로그래밍의 모든 것

"현실 세계는 객체로 이루어져 있다"라는 철학적 명제가 마치 코드로 구현된 것 같은 객체지향 프로그래밍(OOP). 오늘날 대부분의 현대 소프트웨어가 이 패러다임 위에 구축되어 있다는 사실, 알고 계셨나요? 하지만 "객체지향적으로 코딩하라"는 조언을 듣고도 정확히 무엇을 해야 할지 모르겠다면, 걱정하지 마세요. 오늘은 객체지향 프로그래밍의 4대 핵심 특징과 다른 프로그래밍 패러다임까지 비교하며 살펴보겠습니다.

객체지향 프로그래밍이란?

객체지향 프로그래밍(Object-Oriented Programming, OOP)은 데이터와 이를 조작하는 메소드를 하나의 '객체(Object)'라는 단위로 묶어 프로그램을 구성하는 패러다임입니다. 현실 세계의 개념을 코드로 표현하기 쉽게 해주며, 재사용성과 확장성이 뛰어납니다.

대표적인 객체지향 언어로는 Java Python C++ JavaScript 등이 있습니다.

객체지향 프로그래밍의 4대 특징

1. 캡슐화(Encapsulation)

캡슐화는 관련된 데이터와 함수를 하나의 단위로 묶고, 외부에서의 접근을 제한하는 것을 의미합니다. 이는 데이터 은닉(Data Hiding)을 통해 객체 내부 구현을 보호합니다.

// Java 예시
public class BankAccount {
  private double balance; // 외부에서 직접 접근 불가

  public BankAccount(double initialBalance) {
    this.balance = initialBalance;
  }

  public void deposit(double amount) {
    if (amount > 0) {
      balance += amount;
      System.out.println("입금 완료: " + amount);
    }
  }

  public double getBalance() {
    return balance;
  }
}

위 예시에서 balance 변수는 private으로 선언되어 외부에서 직접 접근할 수 없습니다. 대신 deposit()getBalance() 메소드를 통해서만 데이터를 조작하거나 조회할 수 있습니다.

2. 상속(Inheritance)

상속은 기존 클래스의 속성과 메소드를 새로운 클래스가 재사용할 수 있게 해주는 메커니즘입니다. 코드 재사용성을 높이고 계층 구조를 형성합니다.

// Python 예시
class Animal:
  def __init__(self, name):
    self.name = name

  def speak(self):
    pass

class Dog(Animal): # Animal 클래스 상속
  def speak(self):
    return f"{self.name}가 멍멍 짖습니다."

class Cat(Animal): # Animal 클래스 상속
  def speak(self):
    return f"{self.name}가 야옹 웁니다."

dog = Dog("바둑이")
cat = Cat("나비")

print(dog.speak()) # 출력: 바둑이가 멍멍 짖습니다.
print(cat.speak()) # 출력: 나비가 야옹 웁니다.

위 예시에서 DogCat 클래스는 Animal 클래스를 상속받아 name 속성과 speak() 메소드를 재사용합니다. 각 자식 클래스는 speak() 메소드를 자신의 특성에 맞게 재정의합니다.

3. 다형성(Polymorphism)

다형성은 같은 인터페이스나 메소드가 객체의 타입에 따라 다른 동작을 할 수 있게 해주는 특성입니다. 유연하고 확장 가능한 코드를 작성할 수 있게 해줍니다.

// C++ 예시
#include <iostream>
using namespace std;

class Shape {
public:
  virtual double area() {
    return 0;
  }
};

class Rectangle : public Shape {
private:
  double width, height;
public:
  Rectangle(double w, double h) : width(w), height(h) {}
  double area() override {
    return width * height;
  }
};

class Circle : public Shape {
private:
  double radius;
public:
  Circle(double r) : radius(r) {}
  double area() override {
    return 3.14 * radius * radius;
  }
};

int main() {
  Shape* shapes[2];
  shapes[0] = new Rectangle(5, 4);
  shapes[1] = new Circle(3);

  for (int i = 0; i < 2; i++) {
    cout << "도형의 면적: " << shapes[i]->area() << endl;
  }

  return 0;
}

위 예시에서 RectangleCircle 클래스는 모두 Shape 클래스를 상속받고 area() 메소드를 재정의합니다. 실행 시점에 객체의 실제 타입에 따라 적절한 area() 메소드가 호출됩니다.

4. 추상화(Abstraction)

추상화는 복잡한 시스템에서 핵심적인 개념이나 기능만을 간추려 표현하는 것을 의미합니다. 불필요한 세부 사항을 숨기고 중요한 부분만 드러냅니다.

// JavaScript 예시
// 스마트폰의 추상화
class Smartphone {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
    this.isOn = false;
  }

  turnOn() {
    this.isOn = true;
    console.log(`${this.brand} ${this.model} 전원이 켜졌습니다.`);
  }

  turnOff() {
    this.isOn = false;
    console.log(`${this.brand} ${this.model} 전원이 꺼졌습니다.`);
  }

  makeCall(number) {
    if (this.isOn) {
      console.log(`${number}로 전화 연결 중...`);
    } else {
      console.log(`전원이 꺼져 있습니다.`);
    }
  }
}

const myPhone = new Smartphone('iPhone', '13');
myPhone.turnOn();
myPhone.makeCall('010-1234-5678');

위 예시에서 Smartphone 클래스는 스마트폰의 복잡한 내부 동작(하드웨어 작동 방식, 전화 연결 프로토콜 등)을 숨기고, 사용자에게 필요한 기능(전원 켜기/끄기, 전화 걸기)만을 제공합니다.

실전에서의 객체지향 프로그래밍 적용 사례

객체지향 프로그래밍은 다양한 실제 애플리케이션 개발에 광범위하게 사용됩니다:

  • GUI 애플리케이션: 버튼, 텍스트 필드, 창과 같은 UI 요소들이 객체로 표현됩니다.
  • 게임 개발: 캐릭터, 아이템, 환경 요소 등을 객체로 모델링합니다.
  • 웹 개발: MVC(Model-View-Controller) 패턴을 활용한 웹 프레임워크들이 객체지향 원칙을 따릅니다.
  • 기업 소프트웨어: 복잡한 비즈니스 로직을 객체와 클래스로 구조화하여 관리합니다.

객체지향 프로그래밍의 장단점

장점 단점
코드 재사용성 향상 초기 설계 시간이 많이 소요됨
유지보수 용이성 작은 프로그램에서는 오버헤드가 발생할 수 있음
대규모 프로젝트에 적합 학습 곡선이 가파름
현실 세계 모델링이 직관적 객체 간 상호작용이 복잡해질 수 있음
확장성이 좋음 일부 문제 도메인에는 적합하지 않을 수 있음

다양한 프로그래밍 패러다임

객체지향 프로그래밍 외에도 다양한 프로그래밍 패러다임이 존재합니다. 각각의 패러다임은 특정 문제 해결에 더 적합할 수 있습니다.

1. 절차적 프로그래밍(Procedural Programming)

절차적 프로그래밍은 프로그램을 순차적인 함수 호출로 구성하는 방식입니다. 데이터와 함수가 분리되어 있으며, 주로 작은 문제를 해결하는 데 효과적입니다.

대표 언어: C Pascal Fortran

// C 예시
#include <stdio.h>

void printGreeting(char* name) {
  printf("안녕하세요, %s님!\n", name);
}

int add(int a, int b) {
  return a + b;
}

int main() {
  printGreeting("홍길동");
  int result = add(5, 3);
  printf("5 + 3 = %d\n", result);
  return 0;
}
2. 함수형 프로그래밍(Functional Programming)

함수형 프로그래밍은 수학적 함수 개념을 기반으로 하며, 상태 변경과 가변 데이터를 피하고 순수 함수를 사용합니다. 병렬 처리와 테스트가 용이하고 버그가 적은 코드를 작성할 수 있습니다.

대표 언어: Haskell Clojure Erlang F#

-- Haskell 예시
-- 숫자 리스트의 총합 계산
sum' :: [Int] -> Int
sum' [] = 0
sum' (x:xs) = x + sum' xs

-- 리스트의 모든 원소에 2를 곱하기
doubleList :: [Int] -> [Int]
doubleList = map (*2)

-- 리스트에서 짝수만 필터링
evenOnly :: [Int] -> [Int]
evenOnly = filter even

main = do
  print $ sum' [1, 2, 3, 4, 5] -- 출력: 15
  print $ doubleList [1, 2, 3] -- 출력: [2, 4, 6]
  print $ evenOnly [1..10] -- 출력: [2, 4, 6, 8, 10]
3. 논리형 프로그래밍(Logic Programming)

논리형 프로그래밍은 형식 논리에 기반하여 프로그램을 작성하는 패러다임입니다. 사실(facts)과 규칙(rules)을 정의하고, 시스템이 자동으로 답을 추론하도록 합니다. 인공지능, 자연어 처리, 데이터베이스 쿼리 등에 활용됩니다.

대표 언어: Prolog Mercury

% Prolog 예시
% 가족 관계 데이터베이스
parent(john, bob).
parent(john, lisa).
parent(bob, ann).
parent(bob, mike).

% X가 Y의 조부모인지 확인하는 규칙
grandparent(X, Y) :- parent(X, Z), parent(Z, Y).

% 쿼리: john이 조부모인 모든 사람 찾기
% ?- grandparent(john, X).
% X = ann ;
% X = mike ;
4. 데이터 지향 프로그래밍(Data-Oriented Programming)

데이터 지향 프로그래밍은, 데이터와 동작의 분리를 강조합니다. 데이터는 불변하는 것으로 취급하며, 일반적인 데이터 구조와 독립적인 함수들로 프로그램을 구성합니다.

대표 언어: Go Clojure

// Go 예시
package main

import (
  "fmt"
)

// 사용자 데이터 구조
type User struct {
  ID int
  Name string
  Email string
}

// 사용자 정보 출력 함수
func printUserInfo(user User) {
  fmt.Printf("ID: %d, 이름: %s, 이메일: %s\n", user.ID, user.Name, user.Email)
}

// 이메일 유효성 검사 함수
func isValidEmail(email string) bool {
  // 간단한 검증 로직
  return len(email) > 0 && len(email) < 255
}

func main() {
  user := User{
    ID: 1,
    Name: "홍길동",
    Email: "hong@example.com",
  }

  printUserInfo(user)
  fmt.Println("유효한 이메일:", isValidEmail(user.Email))
}
반응형
Posted by no_name
: