• 2021. 9. 27.

    by. 문익점

    반응형

    캡슐화는 객체의 속성(데이터)과 행위(기능)를 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉하는 것입니다. 즉 객체의 기능의 구현체를 외부에서는 볼 수 없도록 감추는 것입니다.

    이점

    1. 외부에 영향 없이 객체 내부 구현을 변경 가능 합니다.
    2. 정보은닉의 의미
      • 객체에 포함된 정보의 손상과 오용을 막을 수 있다.
      • 객체 내부의 조작 방법이 바뀌어도 사용방법은 바뀌지 않는다.
      • 데이터가 바뀌어도 다른 객체에 영향을 주지 않아 독립성이 유지

    캡슐화의 적용

    캡슐화 X

    아래는 캡슐화가 적용되지 않는 코드입니다. 이 코드에서 조건에 대한 변경사항이 생겨 코드를 수정하려고합니다. 캡술화를 하지 않았을때 생길 수 있는 문제들을 설명해보려합니다.

    아래는 레벨 10이상인 회원에게 쿠폰 보상을 지급하는 코드가 있다고 하겠습니다.

    if(user.getLevel() === 10) {
        // 쿠폰 보상 지급
    }

    근데 갑자기 쿠폰 보상 지급 조건이 다음과 같은 수정 사항이 생겼습니다.

    레벨 10이상, 회원 등급 맴버쉽인 경우

    if(user.getLevel() === 10 && user.getMembership() === "MEMBERSHIP") {
        // 쿠폰 보상 지급
    }

    다음과 같이 맴버쉽을 체크하는 코드가 추가 될 것 입니다. 다만 이 코드 한 곳에서만 쓰인다면 다행입니다. 하지만 프로젝트 전체에서 쿠폰 보상 지급 기능을 하는 곳이 여러 군데라면 위 코드가 사용되는 곳이 여러군데 일 것입니다. 이는 쿠폰 지급의 조건이 변경될 때마다 코드가 사용되는 곳을 이곳저곳 찾아 모두 수정해야됩니다. 위 코드는 단적인 예지만 실제 프로덕션에서는 좀 더 복잡한 코드일 것입니다. 그렇다면 간단한 코드 수정에도 많은 시간이 들어가게 됩니다.

    다른 개발자가 만든 레거시라면 더욱요...

    요구사항이 변경되어 다수의 코드의 수정이 발생되어 유지보수가 어려워 집니다. 또한 코드를 수정하는 작업의 시간이 증가하여 작업량이 늘어나 생산성이 떨어지게 됩니다. 이는 객체지향의 장점을 살릴 수 없는 코드가 되는 것입니다.

    캡슐화 O

    한번 위 코드를 캡슐화를 진행시켜 보겠습니다. 캡슐화의 특성 중 하나가 기능의 구현체를 외부에서는 볼 수 없도록 감추는 것이라고 했습니다.

    class User {
        private level: number;
        private memberShip: Membership;
    
        public hasCouponPermission = () 
                => this.level === 10 && this.memberShip === "MEMBERSHIP"
    }

    현재 유저 클래스에서 레벨과 맴버쉽등급(데이터) 그리고 쿠폰 권한 체크 메소드(기능)을 묶어버렸습니다. 쿠폰 인증 체크 기능은 내부에 구현되어 구현체는 외부로부터 감추었습니다. 만약 쿠폰 권한 체크를 캡슐화하지 않는 코드가 아닌 hasCouponPermission()를 이용 하였다면 아래와 같습니다.

    ...
    if(user.hasCouponPermission()) {
        // 쿠폰 보상 지급
    }
    ...

    즉 hasCouponPermission()의 내부 구현만 변경해주면 변경사항에 따라 코드를 수정하기 편합니다. 즉 요구사항의 변화가 코드 영향을 최소화할 수 있습니다. 이곳저곳 돌아다니며 코드에 하나하나 user.getMembership() === "MEMBERSHIP"를 추가하지 않아도 됩니다.

    1. user.getLevel() === 10 && user.getMembership() === "MEMBERSHIP"
    2. hasCouponPermission()

    또한 캡슐화를 진행하게 되면 코드를 읽을 시에 기능에 대해 개발자가 명확하게 이해 할 수 있다는 장점이 있습니다. 1번과 같은 코드는 요구 사항이 무엇인지 개발자가 알아야 이해가능한 코드이지만 2번과 같은 코드는 hasCouponPermission이라는 메소드(기능)의 이름을 읽음으로써 쿠폰을 얻을 권한이 있는지를 체크하는 메소드라는 것을 알 수가 있습니다.

    즉 요구사항에 따른 기능이 무엇인지 메소드 명을 읽음으로써 개발자가 알 수 있으므로 코드를 더 쉽게 읽을 수 있습니다. 이는 생산성 증대로 이어집니다.

    Tell, don't ask (TDA)

    보통 캡슐화를 이야기 할 때 많이 나오는 규칙 중 하나입니다. 객체에게 정보를 요구하지 말고 그냥 행위하도록 시키라는 의미입니다. 즉 데이터를 달라고 하지말고 데이터를 가지고 있으니까 이용해서 "해줘"라고 하는겁니다.

    user.getLevel() === 10 && user.getMembership() === "MEMBERSHIP" 
        => 데이터를 달라고 해서 직접 비교하지 않기!
    user.hasCouponPermission() 
        => 데이터 가지고 있으니까 권한체크 해줘!

    위 코드를 보면 유저 클레스는 레벨과 맴버쉽 정보(데이터)를 가지고 있으니 쿠폰 권한이 있는지 알아와라라고 지시하는겁니다. getLevel(), getMembership()등 데이터를 달라고해서 직접 비교하지말고 데이터를 가지고 있는 객체에게 권한 체크(기능)을 하라고 시키는겁니다.

    정리

    캡슐화의 핵심은 기능의 구현체를 외부에서는 볼 수 없도록 감추는 것입니다. 위에서 볼 수 있듯이 캡슐화는 유지보수 증대와 생산성 증대로 이어집니다. 캡술화는 요구 사항에 변화에 따라서 유연하게 다른 코드에 영향 주지 않으면서 내부 구현을 변경 할 수 있는 유연함을 갖게 됩니다.

    반응형