이미지 출처: freepik.com
관심사의 분리(Separation of Concerns, SoC)는 소프트웨어 개발에서 가장 중요한 설계 원칙 중 하나이며, SOLID 원칙 중 두 가지(단일 책임 원칙(SRP, Single Responsibility Principle)과 인터페이스 분리 원칙(ISP, Interface Segregation Principle))가 바로 이 개념에서 파생되었다. 이 원칙은 프로그램을 여러 독립적인 컴포넌트로 나누어 각각의 컴포넌트가 특정 역할에만 집중하도록 하여, 전체 시스템의 복잡성을 줄이고 유지보수성, 테스트 가능성, 재사용성을 높이는 것을 목표로 한다. SoC는 특히 복잡한 대규모 시스템에서 중요한 역할을 한다.
고객이 프로그램에 새로운 기능을 추가해 달라고 요청했다고 가정해 보자. 이때 프로그램을 수정하면서 고려해야 할 몇 가지 중요한 요소가 있다.
관심사의 분리는 이러한 질문들에 더 긍정적인 답변을 얻을 수 있게 도와준다.
컴퓨터 과학에서 관심사의 분리는 프로그램을 각기 다른 관심사로 나눔으로써 프로그램의 특정 부분이 하나의 관심사만을 다루도록 하는 설계 원칙이다. 이 원칙은 단지 소프트웨어 개발에만 국한되지 않고, 우리의 일상생활에서도 쉽게 찾아볼 수 있는 보편적인 원칙이다.
가정생활에서의 관심사 분리
가장 쉬운 예로 가정생활을 생각해 보자. 집안에서 일어나는 다양한 일들은 서로 다른 사람이나 시스템에 의해 처리된다. 예를 들어, 청소, 요리, 정원 관리, 가계부 작성 등이 있다. 만약 한 사람이 이 모든 일을 혼자 맡게 된다면, 효율성은 떨어지고 실수도 많아질 것이다.
이것이 바로 가정 내에서의 관심사 분리다. 청소는 청소기가, 요리는 주방에서, 정원 관리는 정원사가, 가계부 작성은 가족 중 재정 담당자가 각각 맡아서 처리한다. 각자가 맡은 역할에 집중하므로, 집안일은 더 원활하게 진행된다.
회사에서의 관심사 분리
회사에서도 관심사 분리는 필수적이다. 예를 들어, IT 부서, 인사 부서, 마케팅 부서 등은 각기 다른 역할을 담당한다. IT 부서는 회사의 기술적 요구를 충족시키고, 인사 부서는 직원 관리와 복지를 책임지며, 마케팅 부서는 제품이나 서비스를 홍보한다. 만약 한 부서가 모든 역할을 맡게 된다면, 혼란과 비효율성이 발생할 것이다.
이처럼 회사 내에서는 각 부서가 자신의 역할에 집중하고, 다른 부서와 필요한 부분에서만 협력하는 구조를 가진다. 이는 회사 운영을 더 효율적이고 체계적으로 만들며, 각 부서가 더 높은 전문성을 갖출 수 있게 한다.
프로그램을 작성할 때, 모든 코드를 한 곳에 모아두면 유지보수나 확장이 매우 어려워진다. 예를 들어, 웹사이트를 만들 때 HTML, CSS, JavaScript 코드를 하나의 파일에 모두 포함시키는 것은 작은 프로젝트에서는 가능할 수 있지만, 프로젝트가 커지면 문제가 된다.
이를 해결하기 위해, 우리는 코드를 구조적으로 분리한다. HTML은 웹페이지의 구조를 담당하고, CSS는 스타일을 정의하며, JavaScript는 동적 동작을 처리한다. 이렇게 분리하면 각 부분을 독립적으로 수정하거나 교체할 수 있어, 유지보수가 쉬워지고 코드의 가독성도 높아진다.
<!DOCTYPE html>
<html>
<head>
<style>
body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}
</style>
</head>
<body>
<h1>This is a heading</h1>
<p>This is a paragraph.</p>
</body>
<script>
console.log("hello world")
</script>
</html>
위의 코드에서는 HTML 구조, 스타일링, 스크립트 로직이 모두 한 파일에 포함되어 있다. 이 구조에서는 페이지가 복잡해질수록 코드를 관리하기가 어려워지며, 변경 사항을 적용할 때 오류가 발생할 가능성이 높아진다.
하지만, 다음과 같이 각 부분을 분리할 수 있다.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>This is a heading</h1>
<p>This is a paragraph.</p>
</body>
<script src="logic.js"></script>
</html>
// logic.js
console.log("hello world")
/* styles.css */
body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}
이제 코드는 HTML 구조, CSS 스타일링, JavaScript 로직으로 각각 분리되었다. 이러한 구조에서는 코드가 더 명확해지고, 각 부분을 독립적으로 수정할 수 있어 유지보수와 확장이 훨씬 쉬워진다. 예를 들어, 스타일을 변경하고 싶다면 styles.css
파일만 수정하면 된다. 로직을 변경하고 싶다면 logic.js
파일만 수정하면 된다. 이는 SoC의 기본 원칙을 잘 보여주는 예시다.
코드의 가장 기본적인 단위인 함수에서도 SoC 원칙을 적용할 수 있다. 복잡하고 긴 함수는 여러 책임을 가지고 있을 가능성이 높으며, 이는 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만든다. SoC는 이러한 함수를 더 작은 함수로 분리하여 각각이 명확한 역할을 수행하도록 한다.
function drawCircle() {
// 원을 그리는 코드
}
function drawRectangle() {
// 사각형을 그리는 코드
}
이 두 함수는 모두 그리기 작업을 수행하므로, 이들을 하나의 클래스나 모듈에 포함시키는 것이 자연스럽다. 이를 통해 관련된 기능을 하나로 묶어 응집도를 높일 수 있다.
class Draw {
public function drawCircle() {
// 원을 그리기
}
public function drawRectangle() {
// 사각형을 그리기
}
}
이렇게 하면 그리기 작업과 관련된 기능이 한 곳에 모여 있어 코드가 더 이해하기 쉽고 유지보수하기 쉬워진다.
함수 단위에서 SoC를 적용하는 것뿐만 아니라, 더 큰 단위인 모듈에서도 이 원칙을 적용할 수 있다. 모듈은 일반적으로 관련된 함수나 클래스를 하나로 묶어 더 큰 기능을 제공하는 단위이다. 이 모듈들을 잘 분리하면 시스템 전체의 유지보수성과 확장성이 크게 향상된다.
예를 들어, 어떤 애플리케이션이 사용자 관리, 상품 관리, 주문 관리 등의 기능을 가진다고 가정하자. 각 기능을 독립적인 모듈로 분리하여, 서로 독립적으로 동작할 수 있게 한다면, 한 모듈의 변경이 다른 모듈에 미치는 영향을 최소화할 수 있다.
응집도는 모듈이나 클래스 내의 요소들이 얼마나 밀접하게 관련되어 있는지를 나타내는 척도이다. 응집도가 높다는 것은 그 모듈이나 클래스가 단일한 기능이나 역할에 집중하고 있다는 의미다. 응집도가 낮다면, 그 모듈은 여러 가지 역할을 동시에 수행하고 있을 가능성이 높으며, 이는 코드의 가독성과 유지보수성을 저해한다.
응집도는 그 수준에 따라 다음과 같이 분류할 수 있다.
높은 응집도를 가진 모듈은 다음과 같은 장점을 제공한다.
반대로, 낮은 응집도를 가진 모듈은 다음과 같은 문제를 일으킨다.
결합도는 모듈 간의 의존성 정도를 나타낸다. 결합도가 낮다는 것은 모듈 간의 의존성이 적고, 각 모듈이 독립적으로 동작할 수 있음을 의미한다. 반대로, 결합도가 높으면 모듈 간의 의존성이 커져, 하나의 모듈을 변경할 때 다른 모듈에도 영향을 미치게 된다.
결합도는 그 수준에 따라 다음과 같이 분류할 수 있다.
낮은 결합도를 가진 모듈은 다음과 같은 장점을 제공한다.
높은 결합도를 가진 모듈은 다음과 같은 문제를 일으킨다.
결합도를 줄이기 위한 몇 가지 전략은 다음과 같다.
결합도와 응집도는 프로그래머의 관점에서 코드 작업의 편리성과 생산성에 크게 영향을 미친다. 느슨한 결합과 높은 응집도를 유지하면 코드의 유지보수와 확장이 쉬워지고, 개발 속도도 빨라지며, 팀의 협업도 원활해진다.
모듈이 각각의 역할에 맞게 잘 정의된 메서드와 명확한 API를 가지고 있다면, 프로그램에서 무슨 일이 일어나고 있는지를 이해하기가 훨씬 쉬워진다. 예를 들어, 특정 모듈이 사용자 데이터를 처리하는 역할을 담당한다면, 그 모듈에는 오직 사용자 데이터와 관련된 코드만 있어야 한다. 이렇게 하면 코드를 읽는 사람은 그 모듈이 어떤 일을 하는지 한눈에 파악할 수 있다. 이처럼 명확한 역할 분리가 이루어지면, 코드의 가독성이 높아지고 유지보수가 쉬워진다.
코드를 재사용할 수 있다는 것은 매우 중요한 이점이다. 이를 통해 유지보수 비용을 크게 줄일 수 있다. 예를 들어, 한 번 작성한 코드가 여러 곳에서 재사용될 수 있다면, 그 코드를 수정할 때도 한 곳만 수정하면 된다. 이를 통해 같은 기능을 여러 번 구현할 필요가 없어지고, 버그를 수정하거나 기능을 확장할 때도 수정이 간편해진다. 이는 "DRY(Don't Repeat Yourself)" 원칙을 따르는 것이며, 코드의 일관성을 유지하는 데 큰 도움이 된다.
모듈이 적절하게 독립성을 유지하고 있다면, 그 모듈을 테스트하는 것도 훨씬 쉬워진다. 예를 들어, 특정 모듈이 다른 모듈에 크게 의존하지 않는다면, 그 모듈을 독립적으로 테스트할 수 있다. 이는 전체 시스템을 설정할 필요 없이, 해당 모듈만 테스트할 수 있게 해준다. 실제 모듈 대신 더미 모듈이나 가짜 데이터를 사용해도 테스트가 가능하다. 이렇게 하면 모듈을 블랙박스 테스트(출력만 확인)하거나 화이트박스 테스트(내부 동작까지 확인)할 수 있다. 이를 통해 버그를 조기에 발견하고 수정할 수 있어, 시스템의 안정성을 높일 수 있다.
모듈이 잘 분리되어 있으면, 새로운 기능을 추가하거나 기존 기능을 업데이트하는 작업이 훨씬 빠르게 진행될 수 있다. 변경이 필요한 부분을 명확히 구분할 수 있기 때문에, 변경으로 인해 영향을 받을 수 있는 프로그램의 다른 부분을 쉽게 파악할 수 있다. 이렇게 하면 개발 속도가 빨라지고, 프로젝트의 전반적인 진척도도 더 빨라진다. 또한, 각 모듈이 독립적으로 동작하기 때문에, 변경 사항이 다른 모듈에 미치는 영향을 최소화할 수 있다.
여러 명의 엔지니어가 동시에 개발할 때, 모듈 단위로 작업을 분할하면 효율이 크게 향상된다. 각 엔지니어는 자신이 작업할 모듈을 맡아 독립적으로 작업할 수 있으며, 서로의 작업에 방해가 되지 않도록 조정할 수 있다. 물론, 모듈의 API를 변경해야 할 경우에는 다른 개발자들에게 명확하게 알릴 필요가 있지만, 대부분의 변경 사항은 다른 모듈에 영향을 주지 않으므로 즉각적인 주의가 필요하지 않다. 또한, 좋은 테스트 커버리지가 유지된다면 병렬 개발의 효율성도 극대화될 수 있다. 이는 각 엔지니어가 독립적으로 작업할 때와 동일한 수준의 생산성을 유지할 수 있다는 것을 의미한다.
시스템 설계에서 관심사의 분리(Separation of Concerns, SoC)를 적용하는 것은 각 모듈이 독립적으로 동작하면서도 전체 시스템이 일관되게 기능할 수 있도록 만드는 중요한 전략이다. 모듈이 명확한 목적을 가지고 구성되었다 하더라도, 모듈 간의 상호작용 방식에 대한 전반적인 전략이 없다면 시스템은 복잡해지고 유지보수하기 어려운 구조가 될 수 있다.
시스템 설계의 핵심 목표는 각 모듈이 서로에 대해 인식하는 경계를 명확하게 설정하는 것이다. 이는 모듈 간의 의존성을 최소화하고, 독립적으로 동작할 수 있도록 하는 데 중점을 둔다. 모듈 간의 잘못된 의존 관계는 시스템의 복잡성을 증가시키고, 코드 변경 시 예상치 못한 오류를 발생시킬 수 있기 때문이다.
기존의 아키텍처 패턴들은 이러한 전략을 지원한다. 대표적인 예로 모델-뷰-컨트롤러(Model-View-Controller, MVC) 패턴이 있다. 이 패턴에서는 다음과 같은 규칙을 따른다.
MVC 패턴은 이러한 역할 분리를 통해 시스템의 복잡성을 줄이고, 각 구성 요소가 명확한 책임을 갖도록 한다. 뷰는 모델과 직접 상호작용하지 않고, 항상 컨트롤러를 통해서만 상호작용한다. 이처럼 역할 간의 명확한 경계를 설정함으로써 시스템의 유연성을 높일 수 있다.
모듈을 효과적으로 분리하는 또 다른 방법은 시스템을 계층으로 나누는 것이다. 각 계층은 유사한 역할과 동일한 추상화 수준을 가지며, 계층 간의 통신은 제한된다. 이렇게 하면 각 계층은 서로 독립적으로 동작할 수 있게 되고, 시스템의 복잡성이 줄어든다.
이러한 계층적 구조에서는 하위 계층의 변경이 상위 계층에 영향을 미치지 않도록 설계하는 것이 중요하다. 예를 들어, 네트워킹 서비스가 변경되더라도 비즈니스 로직이나 UI 계층은 그대로 유지될 수 있어야 한다. 이를 통해 시스템의 유연성과 유지보수성을 높일 수 있다.
시스템 설계에서 SoC를 성공적으로 적용하기 위해서는 모듈 간의 의존성을 줄이고, 각 모듈이 독립적으로 동작할 수 있도록 경계를 명확히 설정해야 한다. 이를 위해 MVC와 같은 아키텍처 패턴이나 계층적 설계 방식을 활용할 수 있다. 이러한 전략은 시스템의 복잡성을 줄이고, 유지보수성을 높이며, 테스트를 더 용이하게 만들어 준다.
관심사의 분리(SoC)는 소프트웨어 개발에서 매우 중요한 설계 원칙이다. 이 원칙을 따르면 코드의 유지보수성, 확장성, 테스트 가능성을 크게 향상시킬 수 있다. 코드를 작성하거나 아키텍처를 설계할 때 SoC를 염두에 두고, 결합도를 낮추고 응집도를 높이는 방향으로 설계해야 한다.
- Programming JavaScript Applications
- Separation of concerns
- Coupling (computer programming)
- Cohesion (computer science)
- How do you explain Separation of Concerns to others?
- Separation of Concerns The Simple Way
- Separation of Concerns in Software Design
- What Is Separation Of Concerns?
- 💠 객체의 결합도 & 응집도 의미와 단계 💯 총정리