관심사 분리 (SoC, separation of concerns)

박영준·2023년 2월 25일
0

Programming

목록 보기
1/6

1. 정의

  • 어플리케이션을 구분되는 영역으로 나누며, 각 영역은 분리된 관심사를 가진다.
    (관심 = 프로그램의 기능, 행동, 목적)

  • 하나의 관심사는 하나의 기능(역할)만 가지게 한다.

  • 프로그램을 기능 면에서 가능한 중복이 아닌 여러 모듈로 명확히 나누는 것

  • 소프트웨어 아키텍쳐 디자인(설계) 원칙

  • 큰 문제를 작은 부분들의 문제로 분할
    → 모듈성, 캡슐화가 중요

2. 필요성

  1. 조직화된 시스템을 통해, 변경에 적합한 어플리케이션을 만든다.

  2. 프로그램의 유연성

    만약, 프로그램의 한 모듈에서 [유저의 인터랙션 gui 구현, 검증, db에 데이터 저장, DB에서 데이터 조회, 응답을 위한 html 생성] 을 전부 구현하면 매우 복잡해진다.

3. 사용법

1) 방법

(1) 경계(boundaries) 만들기

무엇 : 어떤 것이든 가능 (논리적/물리적 등...)
방식 : 분리만 되면 모두 가능 (함수, 객체, 컴포넌트 서비스 등...)
범위 : 어디든 가능 (프로젝트, 솔루션, 폴더 구조, 어플리케이션 레이어 등...)

(2) Cohesion and Coupling

높은 응집도 + 낮은 결합도 를 목표로 해야한다.

SoC 의 적용은 결합의 감소와 응집력의 증가라는 두 가지 과정을 포함한다.

  • 응집도

    • 정의
      직무의 집합, 세부사항의 수준 및 지역별 유사성의 척도

    • 예시
      drawCircle 및 drawTriangle 함수 : 도면을 담당하는 동일한 모듈에 속할 만큼 응집력이 있으며, 두 함수를 코드에 서로 가깝게 배치하는 것은 자연스러운 것이다.

  • 결합도

  • 정의
    시스템의 나머지 부분에 대한 부품의 의존도

  • 예시
    drawCircle 및 drawTriangle 함수는 drawCybertruck 함수에 의해 사용될 수 있다.
    drawCybertruck 를 드로우 모듈에 넣을 수 있지만, 이 경우에는 전체 드로우 모듈의 재사용률이 낮아지고 몇 가지 다른 필수 구성요소를 가지게 될 수 있다.
    drawCircle 및 drawTriangle 함수와 drawCybertruck 은 추상화 및 논리 복잡성이 다른 수준에 속하기 때문이다.

    따라서, 어느날 다른 프로젝트에서 드로잉 모듈을 사용할 때, drawCybertruck 모듈이 물리 엔진에 의존하지 않기 때문에 더 쉽게 사용할 수 있을 것이다.

스파게티 코드
알고리즘의 논리를 작성할 때,
"기능이나 모듈 사이를 뛰어다닌다." = "코드의 응집도가 낮다."
이것을 스파게티 코드라고 부른다.

2) 예시

(1) repository

백앤드에서 repository 레이어를 이용하여, 데이터 저장의 관심사를 나눈다.

(2) 프론트엔드와 백엔드의 영역 나눔

프론트엔드의 생태계가 커짐에 따라 예전에는 백앤드와 프론트엔드를 같이 하던 것을 별도로 나눴다.

(3) 기능의 나눔

  1. // 최소와 최대의 합을 구하는 경우
    array_number = [1, 3, 5, 7, 6, 2]
    
    // Soc 적용 X : 4가지의 관심사를 가지고 있다
    def get_min_max_sum (array_numbers)
    
        // 1. 최솟값과 최댓값을 구하기 위해 정렬
        sorted_array = sorted (array_numbers)
    
        // 2. 최솟값 구하기
        min_num = sorted_array[0]
    
        // 3. 최댓값 구하기
        max_num = sorted_array[-1]
    
        // 4. 최솟값과 최댓값을 더하여 return
        return min_num + max_num;
    
    // Soc 적용 O
    def get_min_of_sorted_array (sorted_array):
        return sorted_array[0]
    
    def get_max_of_sorted_array (sorted_array):
        return sorted_array[-1]
    
    def get_min_max_sum (array_numbers):
        sorted_array = sorted (array_numbers)
        min_num = get_min_of_sorted_array (sorted_array)
        max_num = get_max_of_sorted_array (sorted_array)
    
        return min_num + max_num;
  2. public class Board {
    
        private long id;
        private String title;
        private String contents;
        private List<String> comments;
    
    }

    Board 클래스에서는 comments 를 관리하고 있다.
    그러나 만약, 댓글에 댓글(대댓글)을 다는 기능을 새로 구현하고자 한다면?
    → 이를 위해, 개발자는 comments 테이블과 클래스를 만들어서 Board 와 분리하고 작업을 진행해야하므로 번거로워진다.

    이런 문제를 방지하기 위해
    관심사가 같은 것들끼리는 모으고, 다른 것들은 분리해줌으로써 1가지 관심에 효과적으로 집중할 수 있게 해주어야 한다.
    → 만약 board 와 comment 를 분리해 두었다면, 대댓글 기능은 (board 관련 클래스를 볼 필요 없이) comments 클래스에만 한정되어 작업이 진행될 것이다.
    → 즉, 변경 최소화 가능

(4) 수직, 수평 모듈 분할

조금 더 높은 수준에서 각각 논리적 상관관계가 분명한 단일 작업 세트를 만들고, 모듈하에서 함수를 그룹화한다.
→ 덜 밀접하게 관련된 기능을 분리하고, 동일한 고유한 목적을 제공하는 기능들을 그룹화

수평 분할 (Horizontal Separation)

  • 어플리케이션 내에 동일한 역할을 수행하는 기능의 논리적 layer 단위로 앱을 분리하는 프로세스

  • 주의!
    오직 각 layer 에 관련된 것들만 포함해야 한다.
    그 외 다른 컴포넌트와 프로세스는 배제해야 한다.

  1. 예시 : GUI 애플리케이션들이 공통적으로 분리하는 통상적인 분리

  2. 예시 : 서비스 지향 애플리케이션의 통상적인 분리

수직 분할 (Vertical Separation)

  • 어플리케이션을 기능 모듈(애플리케이션 내의 동일한 피쳐나 서브 시스템과 관련된)로 앱을 분리하는 프로세스
    → 각 기능의 의존성과 책임을 분명하게 만든다.
    → 이것은 테스트 & 전반적인 유지보수를 돕는다.

  • 전체 관점에서 어플리케이션의 기능들을 분리하고,
    단일 경계 내에서 interface, business, resource access 관심사를 결부시킨다.

  • 주의!
    논리적 경계는 모듈성의 존재에 대해 암시를 해주지만,
    분리를 나타내기 위해 사용되는 메소드들은 애플리케이션의 실제 배포나 런타임 행위에 대해선 아무런 관련 X

  1. 예시 : 3가지 모듈로 애플리케이션 분리

  2. 예시 : 논리적 경계를 담은 애플리케이션

  3. 예시 : 서로 다른 개발팀에 의해 개발된 모듈들을 탑재한 호스팅 프레임워크

    (일반적으로) 물리적 경계는 add-in(확장 프로그램) or 종합적인 애플리케이션을 개발, 서로 다른 개발 팀에 의해 기능이 관리되는 데에서 사용된다.

수평 & 수직 분할

수직적 분리가 애플리케이션 내의 특정 기능의 수행에 연관된 관심사 세트들로 앱을 분류하긴 하지만,
이것이 관심사 전략의 다른 분리 기법 사용을 배제하는 것은 아니다.

각각의 모듈이 그 자체가 (모듈 내에서 컴포넌트의 역할을 설명하기 위해) 레이어를 사용하게 설계되기도 한다.

4. 장점

  1. 코드의 명료성
    각 모듈이 일련의 메소드(논리적으로 범위가 지정된)를 가진 간결하고 명확한 API 를 가질 때,
    프로그램에서 어떤 일이 벌어지는지 이해하는 것이 훨씬 더 쉽다.
    → 구조화된 프로그램이 된다.

  2. 코드 재사용성 향상 (DRY 원리)
    기능 확장 or 버그 수정 시 마다, 한 곳만 수정하는 것이 더 수월하다.
    테스트 시, 적절한 범위의 기능과 앱의 나머지 부분으로부터 격리된 독립 모듈을 테스트하는 것이 더 수월하다.

  3. 개발 및 유지보수 성능이 좋아진다.

  4. 개발 속도가 빨라진다.
    여러 엔지니어에 의한 동시 개발이 더 용이하다. 서로 간섭하지 않도록 하기 위해 어떤 모듈을 진행 중인지 합의하면 된다.


참고: separation of concerns(관심사 분리)
참고: [Soc]관심사 분리란?
참고: Separation of concerns (관심사의 분리)
참고: [클린코드] 변경을 최소화하는 개발, 관심사의 분리와 변하는 것과 변하지 않는 것의 분리
참고: 관심사의 분리란(Seperation Of Concern) 1편

profile
개발자로 거듭나기!

0개의 댓글