[Swift]프로그래밍 패러다임이란? #1 명령형 프로그래밍 VS 선언형 프로그래밍

Eric·2022년 10월 9일
1
post-thumbnail

도입부

프로그래밍 언어를 공부하다보면 객체 지향 프로그래밍, 함수형 프로그래밍 등과 같은 OO 지향 프로그래밍, OO형 프로그래밍이라는 단어를 한번쯤은 보게 될 것이다.
WWDC 2015에서 Swift를 프로토콜 지향 프로그래밍을 차용한 언어라고 말하기도 하였다.
이 단어들이 의미하는 것은 각 언어가 차용하고 있는 프로그래밍 패러다임이다.


프로그래밍 패러다임(Programming Paradigm)?

어렵게 다가올 수 있지만 각 단어별로 사전적 의미를 살펴보면 이해가 쉽다.

프로그래밍(Programming)

컴퓨터의 프로그램을 작성하는 일. 일반적으로는 프로그램의 작성 방법의 결정, 코딩(coding), 에러 수정 등의 작업 모두를 가리키지만 코딩만을 가리킬 때도 있음.

패러다임(paradigm)

한 시대의 사람들의 견해나 사고를 근본적으로 규정하고 있는 인식의 체계. 또는, 사물에 대한 이론적인 틀이나 체계

쉽게 말해 프로그래밍 패러다임
‘프로그램을 작성하는 체계’, 즉 ‘코딩을 하는 체계’를 의미하는 것임을 알 수 있다.
각 언어마다 차용하는 프로그래밍 패러다임이 달라 코드를 짜는 규칙이 조금씩 다르다.

다중 패러다임 프로그래밍 언어(Multi-Paradigm Languages)

반드시 한 언어에서 하나의 패러다임을 채택해야하는 것은 아니다.
이미 최소 두 가지에서 최대 여덟 가지의 패러다임을 차용한 다양한 언어들이 존재한다.
애플에서 발표한 Swift도 3가지의 패러다임(객체지향, 프로토콜 지향, 함수형)을 차용하고 있는 다중 패러다임 프로그래밍 언어이다.


프로그래밍 패러다임의 종류

프로그래밍 패러다임의 종류는 매우 다양하지만, 대표적으로( 그리고 Swift가 차용한) 몇가지의 프로그래밍 패러다임을 살펴보자.

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

일단 크게 명령형 프로그래밍선언형 프로그래밍으로 나눌 수 있다.

명령형 프로그래밍은 ‘어떻게’ 할 것인가에 가깝고,
선언형 프로그래밍은 ‘무엇을’ 할 것인가에 가깝다.

머리가 더 아파진 거 같지만 끝까지 보면 이해할 수 있을거라 믿고, 아래 예제를 보자.

  • 명령형 프로그래밍
// 파라미터로 받은 배열의 각 요소를 제곱하는 함수이다.
func square(_ intArray: [Int]) -> [Int] {
    var squaredResult: [Int] = []
    
// squaredResult 배열에 intArray 배열의 각 요소를 제곱해서 넣어줘
    for i in 0..<intArray.count {
        squaredResult.append(intArray[i] * intArray[i])
    }
    
    return squaredResult
}
// 파라미터로 받은 배열의 각 요소를 더하는 함수이다.
func add(_ intArray: [Int]) -> Int {
    var addedResult: Int = 0
    
// addedResult 프로퍼티에 intArray 배열의 각 요소를 더해줘
    for i in 0..<intArray.count {
        addedResult += intArray[i]  
    }
    return addedResult 
} 

이 두 예제에는 공통점이 있다.

1. 각 함수가 원하는 기능을 수행하는 방법을 단계적으로 설명한다.
2. 프로그램의 상태를 변경하는 문장을 사용하고 있다.

첫 번째 예제에서는 for문에서 제곱을 하는 방법을 설명하고 있기도 하고( 1. ),
squaredResult라는 정수형 배열을 만든 뒤 수정하고 있기도 하다.( 2. )

두 번째 예제에서도 for문에서 배열의 각 요소를 더하는 방법을 설명하고 있고( 1. ),
addedResult라는 정수 타입의 프로퍼티를 만든 뒤 수정하고 있다.( 2. )

이 특징들은 선언형 프로그래밍과 대조되는 명령형 프로그래밍의 특징이다.

그렇다면 이번엔 선언형 프로그래밍의 시선으로 아래 예제들이 위 두가지 특징을 만족하는지 관찰해보자.

func square(_ intArray: [Int]) -> [Int] {
    return intArray.map{ $0 * $0 }
} 
func add(_ intArray: [Int]) -> Int {
    return intArray.reduce(0) { $0 + $1 }
}

Swift에 내장되어 있는 mapreduce 메서드를 이용했다.
이 예제들을 보면 어떻게 제곱하는지, 어떻게 더하는지 설명하고 있지 않다.
단지 무엇(제곱을, 덧셈을)을 해달라고 서술하고 있다.
그리고 프로그램의 상태도 변경하고 있지 않다.
그럼에도 제곱이 되고, 덧셈이 되고 있는데 사실 mapreduce 메서드는 이미 명령형 방식으로 코드가 작성되어있어서 그렇다. 단지 map, reduce 라는 이름으로 추상화된 것 뿐이다.

파라미터로 받은 배열(intArray)이 수정되면 프로그램의 상태가 변하는 거 아닌가요?

위 예제들만 보면 map 메서드와 square 메서드가 intArray 배열의 요소들을 바꿀 수도 있겠다는 생각이 든다.
후에 더 자세히 다루겠지만 map, reduce는 기존의 데이터를 수정하지 않는다.

func square(_ intArray: [Int]) -> [Int] {
    return intArray.map{ $0 * $0 }
} 
func add(_ intArray: [Int]) -> Int {
    return intArray.reduce(0) { $0 + $1 }
}

let numArray = [2, 4, 6, 8, 10]

print(square(numArray)) // [4, 16, 36, 64, 100]
print(numArray) // [2, 4, 6, 8, 10]

print(add(numArray)) // 30
print(numArray) // [2, 4, 6, 8, 10]

square 하고 add 해도 numArray 배열의 요소는 그대로인걸 확인 할 수 있다.

명령형 프로그래밍과 선언형 프로그래밍의 차이를 설명할 때 요리 레시피로 빗대어 설명하기도 한다.

명령형 프로그래밍의 관점으로 라면을 끓이는 순서를 설명하면,

1. 냄비에 물 550mL를 넣는다.
2. 물을 100C°까지 가열한다.
3. 100C°에 도달하면 스프,, 건더기스프 순서로 냄비 안에 넣고, 
   50초 뒤 계란을 푼다.
4. 계란이 반숙 상태가 되면 return 라면
선언형 프로그래밍의 관점으로 라면을 끓이면,

(라면 레시피를 주며)

1. 라면을 주세요. return 라면

참고

https://boxfoxs.tistory.com/430

잘못된 내용이 있다면 어떤 방식으로든 피드백 부탁드립니다.

profile
IOS Developer DreamTree

0개의 댓글