본문 바로가기
개발 상식 🥕

[면접을 위한 CS 전공지식 노트] 프로그래밍 패러다임

by 킹우현 2024. 7. 5.

0. 프로그래밍 패러다임이란 ?

프로그래머에게 프로그램을 어떻게 바라볼지에 대한 관점을 제공하는 개발 방법론
 

 
프로그래밍 패러다임은 크게 '선언형' '명령형'으로 나누며, 선언형은 함수형이라는 하위 집합을 갖는다. 또한 명령형은 다시 객체지향과 절차지향으로 나뉜다.
 

1. 선언형 프로그래밍과 함수형 프로그래밍

선언형 프로그래밍(Declarative programming)이란 어떻게(How)보다는 무엇(What)을 수행할 것인지에 집중하는 프로그래밍 방식
 

함수형 프로그래밍의 개념과 특징

자료 처리를 수학적 함수의 계산으로 취급하는 프로그래밍 패러다임

 
함수형 프로그래밍(Functional programming)은 선언형 패러다임의 일종으로, 작은 순수 함수들을 블록처럼 쌓아서 로직을 구현하고 고차 함수를 통해 재사용성을 높인 프로그래밍 패러다임이다. 함수형 프로그래밍이 가진 특징은 다음과 같다.

순수 함수

// Not pure function
val name = "BuNa"

function printHello() {
    println("Hello, $BuNa")
}


// pure function
function printHello(name: String) {
    println("Hello, $name")
}

function add(a: Int, b: Int): Int = a + b

 
순수 함수는 오직 입력으로 들어오는 파라미터에만 의존하는 함수를 의미한다. 순수 함수는 함수 자체가 독립적이기 때문에 Side-Effect가 발생하지 않는다.
 
또한, Thread safe를 보장할 수 있기 때문에 synchronized 등과 같은 별도의 동기화 없이 진행할 수 있다. (병렬성, 동시성 문제 해결)
 

불변

순수함수는 변경 불가능한(불변) 값을 활용한다. 불변은 값이 변하지 않기 때문에 시간적 결합(시간이 지남에 따라 어떤 데이터가 변할 수 있음)을 고려하지 않아도 된다는 장점을 가지고 있다.

따라서 불변은 값을 예측 가능하게 해주며, 신뢰를 보장한다.
 

일급 객체

// 함수를 인자로 전달
fun printName(getName: () -> String)) {
    println(getName())
}

printName {
    "BuNa"
}

 
함수형 프로그래밍에서는 함수가 일급 객체(first-class citizen)의 역할을 한다.

함수를 일급 객체로 활용이 가능할 경우, 함수를 변수나 메서드에 할당하거나 / 함수를 인자로 전달받거나 / 함수를 반환 값으로 활용하는 것이 가능하다.
 
여기서 람다(Lambda ; 익명 함수)고차 함수(Higher order function ; 함수에서 다른 함수를 인자로 전달받는 함수) 개념이 사용된다.
 

참조 투명성

// 올바르지 않은 참조 투명성 사례
data class Cat(var age: Int) {
    fun addAge(value: Int): Int {
        age += value
        return age
    }
}

// 올바른 참조 투명성 사례
data class Cat(val age: Int) {
    fun addAge(value: Int): Cat {
        return Cat(age + value)
    }
}

 
참조 투명성이란 동일한 인자에 대해 함수를 실행했을 때, 어떠한 상태 변화 없이 항상 동일한 결과를 반환하여 결과를 (투명하게) 예측할 수 있다는 의미이다.
 
정리하자면, 함수형 프로그래밍은 일급 객체의 특징을 가지고 있으며, 부수 효과가 없는 순수 함수를 사용하여 참조 투명성을 지킬 수 있는 프로그래밍 패러다임이다.

코드의 가독성이 높아지고 유지보수성이 높다는 장점이 있지만, 외부 혹은 내부 데이터의 상태를 조작할 수 없다는 단점이 있다.
 
ex) 배열의 요소를 순차적으로 순회하며 숫자든 배열이든 객체든 하나의 값으로 줄여 return 하는 함수(JS의 함수는 일급 객체이므로 객체지향 프로그래밍보다는 함수형 프로그래밍 방식이 선호됨)
 

2. 명령형 프로그래밍과 객체지향 프로그래밍(Object-Oriented Programming, OOP)

명령형 프로그래밍이란 코드가 ‘어떻게(How) 동작해야 하는지’를 작성하는 프로그래밍 패러다임


객체지향 프로그래밍의 개념

프로그래밍에 필요한 데이터를 추상화시켜 상태와 행위를 가진 '객체'로 만들고, 이러한 객체들간의 상호작용을 바탕으로 로직을 구성하는 패러다임

 
객체지향 프로그래밍은 객체 지향적 설계를 통해 코드의 재사용을 통해 반복적인 코드를 최소화하고, 다 유연하고 변경이 용이한 프로그램을 만들 수 있다.
 

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

객체 지향 프로그래밍의 4가지 특징 추상화, 상속, 다형성, 캡슐화이다.

  • 추상화(Abstraction) : 객체의 공통적인 속성과 행위를 추출하여 정의하는것
  • 상속(Inheritance) : 하위 클래스가 상위 클래스의 속성과 행위를 물려받는 것
  • 다형성(Polymorphism) : 어떤 객체의 속성이나 행위가 상황에 따라 여러 가지 형태를 가질 수 있는 성질
    • 오버라이딩(Overriding) : 상위 클래스가 가지고 있는 메소드를 하위 클래스가 재정의해서 사용하는 것
    • 오버로딩(Overloading) : 같은 이름의 메서드가 인자의 개수나 자료형에 따라 다른 기능을 하는 것
  • 캡슐화(Encapsulation) : 클래스 안에 서로 연관있는 속성과 기능들을 하나의 캡슐로 관리하여 데이터를 외부로부터 보호하는 것
    • 데이터 보호(data protection) – 외부로부터 클래스에 정의된 속성과 기능들을 보호
    • 데이터 은닉(data hiding) – 내부의 동작을 감추고 외부에는 필요한 부분만 노출

 객체지향 프로그래밍 설계 원칙(SOLID)

  1. 단일 책임 원칙 (SRP, Single Responsibility Principle)
    • 하나의 클래스는 단 하나의 책임만 가져야 한다.
    • 단일 책임 원칙을 지키지 않을 경우 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향이 갈 수 있다.
  2. 개방-폐쇄 원칙 (OCP, Open/Closed Principle)
    • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
    • 유지보수 사항이 생긴다면 코드를 쉽게 확장할 수 있도록 하고, 수정할 때는 닫혀있어야 한다.
    • 즉, 기존의 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야 한다.
  3. 리스코프 치환 원칙 (LSP, Liskov Substitution Principle)
    • 프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
    • 부모 객체를 자식 객체로 치환해도 부모 객체를 사용하는 프로그램은 정상적으로 동작해야 한다.
  4. 인터페이스 분리 원칙 (ISP, Interface Segregation Principle)
    • 하나의 일반적인 인터페이스보다 클라이언트를 위한 구체적인 여러 개의 인터페이스를 만들어야 한다.
  5. 의존관계 역전 원칙 (DIP), Dependency Inversion Principle)
    • 추상화에 의존해야지 구체화에 의존하면 안된다.
    • 즉, 상위 계층은 하위 계층의 변화에 대해 독립적이어야 하고 하위 계층은 상위 계층에서 정의한 추상 타입에 의존해야 한다.

코드의 재사용성과 유지보수성이 높다는 장점이 있지만, 처리 속도가 상대적으로 느리고 설계에 많은 시간이 소요된다는 단점이 있다.

 

3. 절차형 프로그래밍(Procedural Programming)

수행되어야 할 기능을 순차적으로 처리하는 것 중요시 여기며, 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법

 
코드의 가독성이 좋고 실행 속도가 빠르다는 장점이 있지만, 모듈화하기가 어렵고 유지보수성이 떨어진다는 단점이 있다.
 

4. 결론

함수형 프로그래밍과 명령형 프로그래밍은 서로 다른 프로그래밍 패러다임이지만 둘을 혼용할 수 있다. 따라서 프로그램의 요구사항과 상황에 따라 적절한 패러다임을 선택할 필요가 있다.