336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

아래 코드를 보자. 

let total = 0;
let item = [
    {name : '새우깡', price: 100},
    {name : '고래밥', price: 200},
    {name : '꼬북칩', price: 300},
    {name : '하이트맥주', price: 400},
];

const addItem = (name, price) => {
    item.push({
        name : name,
        price : price
    });
}


addItem('땅콩', 400);

이전 글과 같은 이유로, addItem 은 액션이다. 

 - 암묵적 입/출력이 존재

 - 실행 시점 및 횟수에 영향을 받음

 

위를 "계산" 으로 바꾸려면 

 - 암묵적 입/출력을 명시적 입/출력으로 변경 

 - 실행 시점 및 횟수에 영향이 없도록 변경 

 

이전 글 처럼, 먼저 암묵적 입/출력을 명시적 입/출력으로 바꿔보면

let total = 0;
let item = [
    {name : '새우깡', price: 100},
    {name : '고래밥', price: 200},
    {name : '꼬북칩', price: 300},
    {name : '하이트맥주', price: 400},
];

const addItemCalc = (items, name, price) => {
    items.push({
        name : name,
        price : price
    });
    
    return items;
}

item = addItemCalc(item, '땅콩', 400);

console.log(item);

 명시적 입/출력으로 변경하였다. 암묵적 입/출력 문제가 사라진 듯 하지만, 암묵적 출력 문제는 존재한다.  

문제는 items 파라미터는 '참조' 로 전달이 되기 때문에, 함수 내 변경사항이 원본에 적용이 된다.

// items 파라미터는 참조로 전달된다, 즉 items 에 뭔가를 추가한다면,
// 파라미터로 전달된 원본도 같이 변경된다. 
// return 값이 존재하지만, 암묵적 출력은 여전히 남아있다.
const addItemCalc = (items, name, price) => {
    items.push({
        name : name,
        price : price
    });
    
    return items;
}

함수의 실행이, 함수 외부의 변수와 '완벽하게' 차단된다면 부수효과(side effect) 억제효과도 있고, 그만큼 테스트와 유지보수 용이성도 증가할 것이다. 

해결방법 중 하나는 '복사본 리턴' 이다. 

 

const addItemCalc = (items, name, price) => {
    let result = items.slice(); //여기서 복사본을 만든다.
    result.push({
        name : name,
        price : price
    });
    
    return result;
}

복사본을 만든 후, 배열을 변경 하고, 그 후에 변경된 배열을 리턴한다. 

 

위와같이 구현하면, 큰 자료구조라 하더라도, 조작 시, 로직과 데이터를 완벽하게 분리 해낼 수 있다. 

 

결론

동작을 계산으로 바꾸는 첫번째 방법 - 변경시에는 복사본을 사용하고 변경된 데이터를 리턴하라.

 

추가

- 객체 복사 시 성능 이슈가 있을 것이라 생각되는 분이 많을 듯 하다. 자바스크립트에서는 복사 시 기본적으로 얕은복사를 사용하고, 깊은 복사는 loadsh 등의 라이브러리를 사용하여 처리한다. 성능상의 이슈는 그리 크지 않은것으로 보이며, 필요할 경우에만 깊은 복사를 사용하면, 발생할 수 있는 성능상의 이슈를 줄일 수 있을 것이다. 

  참고 : https://jess2.xyz/JavaScript/copy/

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

아래 코드를 보자, 동작(Action) 이 있는 코드다.

let total = 0;
let item = [
    {name : '새우깡', price: 100},
    {name : '고래밥', price: 200},
    {name : '꼬북칩', price: 300},
    {name : '하이트맥주', price: 400},
];

const getTotalAmount = () => {
    for(let i = 0; i < 4; i++) {
        total += item[i].price;
    }
    console.log(`total: ${total}`)
};


getTotalAmount();

getTotalAmount 는 total에 item 배열의 price 값을 모두 더하는 간단한 함수.

 

위 코드에서 getTotalAmount 의 특징은 

1. 암묵적인 입/출력이 있다. 

  - 암묵적 입력 : item

  - 암묵적 출력 : total

2. 실행 횟수와 시점에 영향을 받는다. 

  - getTotalAmount 를 두번 호출한다면 item 배열의 요소를 두번 순회, total 값은 2000 이 된다. 

 

때문에, getTotalAmount 는 '동작' 이고, getTotalAmount 의 유지보수 및 테스트를 위해서는 코드 전체를 확인한 후, 

item 과 total 의 값 변경에 신경쓰며 작업해야한다.

작은 프로젝트에서야 저렇게 하는게 빠르고, 신경쓸 부분도 적기 때문에 상관이 없지만, 

프로젝트가 증가하고, 같이 일할 사람들이 생길수록 저런 부분은 복잡성을 증가시키고, 여러 개발자들이 같은 작업을 계속하게된다.

(암묵적 입/출력 분석 및 상태 변경 처리 등...)

 

위 코드에서 암묵적 입/출력을 명시적 입/출력으로만 변경하여도, 유지보수 시 작업의 복잡도를 줄일 수 있다.

//getTotalAmountCalc 의 계산버전
const getTotalAmountCalc = (itemList) => {
    let result = 0;
    for(let i = 0; i < 4; i++) {
        result += itemList[i].price;
    }
    return result;    
}

getTotalAmountCalc 함수가 갖는 특징 

1. 명시적인 입/출력이 생김 

  - 입력: itemList

  - 출력: result 

2. 실행 횟수와 시점의 영향을 받지 않음 

  - 100 번을 실행해도 itemList 가 같으면 항상 같은 값이 출력됨 

 

위 1,2 번의 특징으로 인해 갖는 장점 

1. 코드 테스트 부하가 상대적으로 감소 

  - 입력값에만 영향을 받으므로, 개발자가 원하는 특정 상황에서 테스트 가능

2. 유지보수 시 공수 감소 

  - 문서에는 '입력' 과 '출력' 타입 및 동작만 기술하면, 이후 개발자는 입출력만 신경써서 개발하면 된다.

    (정 못믿겠으면, 개발 전에 테스트용 입력값 갖고 테스트만 하면됨.)

 

위 1,2 번의 특징으로 인해 갖는 단점 

1. 코드량 증가 

  - 함수 첫부분 선언코드, 마지막의 return 코드 

2. 메모리 추가사용 

  - 함수 안의 지역변수 선언, 지금은 작은 사이즈라 상관없지만, 크기가 자꾸 커지면???

 

단점이 없지는 않지만, 향후 정리할 '얕은 복사' 와 '방어적 복사' 등을 사용하고, 

요즘 GC 들은 많은 부분에서 최적화가 이루어지고 있기 때문에, 기가바이트 급의 대용량이 아니라면, 

저 단점들은 '투자' 의 개념으로 생각하는게 좋지 않을까 한다. 

 

결론

동작을 계산으로 바꾸는 첫번째 방법 - 입/출력을 명시적으로 선언하라

  => 비지역 변수 read 는 함수의 파라미터로, 비지역변수 write 는 함수의 리턴값으로 바꿀 것.

 

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

코드는 세가지 종류로 구분할 수 있다.

 

1. 동작(Action) 

  - 실행 시간과 횟수에 영향을 받음

  - 같은 코드라 해도, 나오는 결과가 다를 수 있다. 

  - ex) 메일을 보내는 코드, DB 에 기록하는 코드

  - 명시적 입/출력(함수 파라미터, 리턴값)과 암묵적 입/출력이(전역변수, 객체변수 등의 비지역변수) 섞여있음

 

2. 계산(Calculation)

  - 입/출력이 모두 명시적입 

  - 출력값은 입력값에 대해서만 영향을 받음

  - 입력값이 같으면 항상 같은 값을 출력함

 

3. 데이터 

  - 이벤트 실행의 결과, 

  - 우리가 흔히 아는 변수(리스트, 값 등...)

 

소프트웨어 개발에서 '액션' 이 갖는 특징 

1. 구현 / 설계가 상대적으로 용이함 

  - C/C++, JAVA 구현하듯이 절차적으로 구현하면 됨 

2. 유지보수 및 테스트의 어려움 

  - 소프트웨어 규모가 증가할수록 많은 암묵적 입력이 존재(복잡성이 증가함) 

  - 테스트 자동화 진행 시, 모든 암묵적 입력값을 설정해줘야 함.

 

소프트웨어 개발에서 '계산' 이 갖는 특징 

1. 구현의 상대적인 까다로움

  - 초반 적응의 어려움 (방어적 복사 등... )

2. 유지보수 및 테스트의 상대적인 용이함 

  - 입력값이 같으면 출력값 또한 같다. (-> 유닛 테스트 용이)

 

때문에, 소프트웨어 개발 시, '액션' 의 갯수는 최소화하고, 가능하면 '계산' 을 사용하는게

유지보수에 편할 수 있음.

 

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,