본문 바로가기

개발/JavaScript

Javascript로 작성하는 선언적 프로그래밍(Declarative Programming)

선언적 프로그래밍 이라는 개념을 Dev Matching의 해설을 보며 처음 접했다. 장점이 많은 프로그래밍 패러다임이여서 그것에 대해 알아보고 코드에 어떻게 적용시킬지 알아보자.

 

선언적 프로그래밍(declarative programming)

  • What- What to do
  • 무엇을 하는지에 초점을 맞추는 것 

명령형 프로그래밍(imperative programming)

  • How
  • 어떻게 할건지를 순서대로 나열한 것

추상적인 예

집에 가는 경우

  • 선언적 방식 : 서울특별시 왕십리역입니다.
  • 명령형 방식 : 주차장 북쪽 출구를 나와 왼쪽으로 가세요. 12번가 출구에 도착할 때까지 15번 북쪽 도로를 타세요. 이케아를 끼고 우회전하세요. 직진하여 첫 번째 신호등에서 우회전 하세요. 다음 신호등을 지나 좌회전을 하세요.

선언적 프로그래밍의 장점

  • 가독성
  • 재사용성
  • 참조 투명성 
  • 대체 가능성

Javascript에서 실제 구현 예제

1. 배열 반복하며 값을 가공할때 - Array.map() 

명령형 프로그래밍

// 1
const arr = [1, 2, 3, 4];
const doubleArr = [];
for(let i = 0; i < arr.length; i++){
    doubleArr.push(arr[i] * 2);
}
// console.log(doubleArr) // [2, 4, 6, 8]

// 2
const n = [-9, 87, 72, 452, 32, -9]
for(let i = 0; i < n.length; i++) {
    console.log(n[i])
}

 

선언적 프로그래밍

// 1
const arr = [1, 2, 3, 4];
const doubleArr =  arr.map(i => i * 2)
console.log(doubleArr) // [2, 4, 6, 8]

// 2
const n = [-9, 87, 72, 452, 32, -9]
const z = n.map(v => (v * 2) - 1)

 

선언적 프로그래밍이 좀더 가독성이 좋고 선언적이다.

가급적 for loop은 사용하지 말자.

 

array.map?

더보기

array.map의 기본적인 사용법

const numbers = [1, 2, 3, 4]; 

numbers.map((number, index, source) => { 
    // number: 요소값 
    // index: source에서 요소의 index
    // source: 순회하는 대상 
    console.log(number); // 1 
    console.log(index); // 0 
    console.log(source); // [1] 
    return number * number; 
});

 

결과값 

1
0
[1, 2, 3, 4]
2
1
[1, 2, 3, 4]
3
2
[1, 2, 3, 4]
4
3
[1, 2, 3, 4]

 

 


2. 배열 반복하며 if문 실행할 때 - Array.filter()

 

명령형 프로그래밍

// 1
const numbers = [1, 2, 3, 4];
const result = [];

for(let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        result.push(numbers[i]);
    }
}

// 2
const numbers = [1, 2, 3, 4, 5];
const result = [];

for (i = 0; i< numbers.length; i++) {
    const number = numbers[i];

    if (number > 3) {
        result.push(number);
    }
}

 

선언적 프로그래밍

// 1
const numbers = [1, 2, 3, 4];
const evenNum = numbers.filter((number) => number % 2 === 0);

// 2
const numbers = [1, 2, 3, 4, 5];
const result = numbers.filter((number) => number > 3);

가독성 업업

 

array.filter?

더보기

array.filter의 기본적인 사용법

 

const numbers = [1, 2, 3, 4, 5];
numbers.filter((number, index, source) => {
    // index: 요소값
    // index: 요소의 index
    // source: 순회하는 대상
    console.log(number)
    console.log(index)
    console.log(source)
})

 

결과값

1
0
[1, 2, 3, 4, 5]
2
1
[1, 2, 3, 4, 5]
3
2
[1, 2, 3, 4, 5]
4
3
[1, 2, 3, 4, 5]
5
4
[1, 2, 3, 4, 5]

 

더 나아가 재사용이 가능하게 만들기

const isNumberEven = (number) => number % 2 === 0;
const fn = (numbers) => numbers.filter(isNumberEven);

let numbers = [1, 2, 3, 4];
console.log(fn(numbers))
console.log(fn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))

 


3. 배열의 값을 단일 값으로 줄일때(값 합산) - Array.reduce()

 

명령형 프로그래밍

const numbers = [1, 2, 3, 4];
let total = 0;

for (let number of numbers) {
    total += number;    
}

 

선언적 프로그래밍

const numbers = [1, 2, 3, 4];
const total = numbers.reduce((total, number) => total += number, 0);

 

array.reduce?

더보기

array.reduce의 기본적인 사용법

 

예시1 - accumulator가 있을 때

// accumulator가 있을 때
const numbers = [1];

const result = numbers.reduce((acc, number, index, source) => {
    // accumulator : 누적값(초기값이 정해지지 않은 경우, 배열 인자의 첫번째 값 사용)
    // number : 요소값
    // index : 요소의 index
    // source : 순회하는 대상
    console.log(acc);
    console.log(number);
    console.log(index);
    console.log(source);
    return acc + number;
}, 10)

console.log(result)

 

결과값

10
1
0
[1]
11

 

예시2 -  accumulator가 없을 때

 

// accumulator가 없을 때
const numbers = [1];

const result = numbers.reduce((acc, number, index, source) => {
    // accumulator : 누적값( 초기값이 정해지지 않은 경우, 배열 인자의 첫번째 값 사용)
    // number : 요소값
    // index : 요소의 index
    // source : 순회하는 대상
    console.log(acc);
    console.log(number);
    console.log(index);
    console.log(source);
    return acc + number;
}, 0)

console.log(result)

 

 결과값

0
1
0
[1]
1

 

Review

명령형 프로그래밍

const numbers = [1, 2, 3, 4];
 
const numbersDoubled = [];
for (let i = 0; i < numbers.length; i++) {
    numbersDoubled.push(numbers[i] * 2);
}

const evenNumbers = [];
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        evenNumbers.push(numbers[i]);
    }
}
    
let numbersTotal = 0;
for (let number of numbers) {
    numbersTotal += number;
}

console.log(numbersDoubled) // [2, 4, 6, 8]
console.log(evenNumbers) // [2, 4]
console.log(numbersTotal) // 10

 

선언적 프로그래밍

const numbers = [1, 2, 3, 4];

const numbersDoubled = numbers.map(number => number * 2)
const evenNumbers = numbers.filter(number => number % 2 === 0); 
const numbersTotal = numbers.reduce((numbersTotal, number) => numbersTotal += number, 0)

console.log(numbersDoubled) // [2, 4, 6, 8]
console.log(evenNumbers) // [2, 4]
console.log(numbersTotal) // 10

 

일단 배열 위주로 알아보았는데

객체도 같다. for문을 전혀 사용하지 않도 선언형 대체제들이 많이 있다.

이곳을 참고하여 객체 순회 또한 코드에 적용시키는게 좋을듯 싶다. 

https://peter-cho.gitbook.io/book/3/3_5#undefined-1

 

선언형으로 대체 가능한 문법들 정리 - 실용주의 프런트 엔드 개발

range(10) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

peter-cho.gitbook.io

 

 

Reference

https://www.bitovi.com/blog/understand-declarative-vs-imperative-code-using-array-functions

https://dev.to/adnanbabakan/declarative-programming-with-javascript-2h97