[Kotlin] 의존성이란 무엇인가? 강한 결합과 느슨한 결합을 통해 이해하는 의존성

2024. 11. 8. 20:30· Spring/Dependency Injection
목차
  1. 의존성이란?
  2. 강한 결합을 통해 이해하는 의존성
  3. 강한 결합이란?
  4. 강한 결합의 한계
  5. 느슨한 결합을 통해 강한 결합의 문제 해결하기
반응형

의존성이란?

소프트웨어 개발에서 의존성(Dependency)이란 한 모듈이 다른 모듈의 기능을 사용하거나, 하나의 객체가 다른 객체의 기능을 사용하는 것을 뜻한다. 예를 들어 A 객체의 기능을 실행할 때 B 객체가 필요하다면, "A 객체는 B 객체에 의존한다"라고 한다.

 

 

의존성은 의존하는 정도에 따라 강한 결합과 느슨한 결합으로 나눌 수 있다. 이어서 이 두가지 개념을 사용해 의존성에 대해 이해해보도록 하자.

 

강한 결합을 통해 이해하는 의존성

강한 결합이란?

우리가 SNS앱을 개발하는데, 유저에게 푸시 알림을 보내는 서비스를 만든다고 해보자. 그러면 이런 역할을 하는 PushNotificationService는 다음과 같이 만들 수 있다. 

class PushNotificationService {
    fun sendNotification(message: String, receiverId: String) {
        println("Sending push notification to $receiverId: $message")
    }
}

 

PushNotificationService는 sendNotification이라는 함수를 가진 클래스로, 이 함수는 message를 인자로 받아, "Sending push notification to $receiver: $message"를 출력한다.

 

이제 이 PushNotificationService을 사용해 댓글이 달렸을 때 유저에게 푸시 알림을 보내도록 해보자.

이를 위해서는 코멘트에 새로운 이벤트가 발생했을 때 처리하는 역할을 하는 NewCommentEventHandler을 다음과 같이 만들고 푸시 알림을 보내는 PushNotificationService 객체를 주입 받으면 된다.  이후 내부의 handle 함수가 호출됐을 때 PushNotificationService가 sendNotification을 실행하도록 하면 된다.

class NewCommentEventHandler(
    private val notificationService: NotificationService
) {
    fun handle(comment: String, ownerId: String) {
        notificationService.sendNotification(
            receiverId = ownerId,
            message = "New comment registered: $comment"
        )
    }
}

 

이제 이 NewCommentEventHandler는 다음과 같이 사용될 수 있다.

fun main() {
    val newCommentEventHandlerUsingPush = NewCommentEventHandler(PushNotificationService())
    newCommentEventHandlerUsingPush.handle(ownerId = "ownerId", comment = "안녕하세요")
}

 

위의 코드에서 볼 수 있듯이 NewCommentEventHandler 클래스는 PushNotificationService 클래스를 직접 사용하고 있다. 

 

 

이렇게 하나의 클래스가 다른 클래스를 직접 참조하는 경우 이 둘은 강하게 결합되어 있다고 한다. 이렇게 강하게 결합된 클래스는 코드의 유연성을 떨어뜨리고, 변경에 취약하게 된다. 왜 그런지 이어서 살펴보자.

 

강한 결합의 한계

강하게 결합된 경우 변경에 취약하다. 예를 들어 새로운 코멘트가 발생했을 때 푸시가 아닌, 카카오톡을 통해 알림을 줘야 한다는 요청이 들어왔다고 해보자. 그런 경우 먼저 KakaoMessageService가 다음과 같이 만들어지고,

class KakaoMessageService {
    fun sendMessage(receiverId: String, message: String) {
        println("Sending Kakao message to $receiverId: $message")
    }
}

 

위의 NewCommentEventHandler는 다음과 같이 변경돼야 한다. 

class NewCommentEventHandler(
    private val kakaoMessageService: KakaoMessageService
) {
    fun handle(ownerId: String, comment: String) {
        kakaoMessageService.sendMessage(
            receiverId = ownerId,
            message = "New comment registered: $comment"
        )
    }
}

 

실행 코드도 다음과 같이 바뀌어야 한다.

fun main() {
    val newCommentEventHandlerUsingKaKao = NewCommentEventHandler(KakaoMessageService())
    newCommentEventHandlerUsingKaKao.handle(ownerId = "ownerId", comment = "안녕하세요")
}

 

하나의 요구사항이 들어왔는데, 전체 코드가 바뀌었다. 이유는 NewCommentEventHandler가 PushNotificationService에 강하게 결합되어 있었기 때문이다. NewCommentEventHandler는 이번에 만든 KakaoMessageService와도 강하게 결합되어 있기 때문에 이후 다른 변경사항이 생긴다면 다시 전부 변경해야 한다.

 

 

그렇다면 이 문제를 해결하기 위해서는 어떻게 해야할까? 바로 이를 해결하기 위한 방법은 느슨한 결합을 사용하는 것이다. 이어서 이 방법에 대해 알아보자.

 

느슨한 결합을 통해 강한 결합의 문제 해결하기

느슨한 결합이란 인터페이스를 사용해 의존성을 줄이는 것이다. 기존에 강한 결합에서 NewCommentEventHandler가 PushNotificationService나 KakaoMessageService 같은 특정 클래스에 의존해 생기던 문제를, 중간에 인터페이스를 하나 두고 이 인터페이스에만 의존하도록 함으로써 문제를 해결하는 것이다.

 

예를 들어 NewCommentEventHandler와 PushNotificationService, KakaoMessageService의 중간에 NotificationService라는 인터페이스를 두고, PushNotificationService와 KakaoMessageService가 이 인터페이스를 구현하게 하면 된다.

interface NotificationService {
    fun sendNotification(receiverId: String, message: String)
}
class PushNotificationService : NotificationService {
    override fun sendNotification(message: String, receiverId: String) {
        println("Sending push notification to $receiverId: $message")
    }
}
class KakaoMessageService: NotificationService {
    fun sendMessage(receiverId: String, message: String) {
        println("Sending Kakao message to $receiverId: $message")
    }

    override fun sendNotification(receiverId: String, message: String) {
        sendMessage(receiverId = receiverId, message = message)
    }
}

 

이후 NewCommentEventHandler도 NotificationService에만 의존하도록 하면 된다.

class NewCommentEventHandler(
    private val notificationService: NotificationService
) {
    fun handle(comment: String, ownerId: String) {
        notificationService.sendNotification(
            receiverId = ownerId,
            message = "New comment registered: $comment"
        )
    }
}

 

그러면 의존성 그래프는 다음과 같은 모양이 된다.

 

 

이렇게 되면 NewCommentEventHandler는 PushNotificationService나 KaKaoMessageService 양쪽을 모두 사용할 수 있게 되며, 이를 통해 유연성이 올라간다.

fun main() {
    val newCommentEventHandlerUsingPush = NewCommentEventHandler(PushNotificationService())
    newCommentEventHandlerUsingPush.handle("안녕하세요")
    
    val newCommentEventHandlerUsingKaKao = NewCommentEventHandler(KakaoMessageService())
    newCommentEventHandlerUsingKaKao.handle("안녕하세요")
}

 

이렇게 인터페이스를 의존하도록 만드는 것을 의존성을 느슨하게 만든다 혹은 느슨하게 결합하도록 한다고 한다. 하지만, 이렇게 느슨한 결합은 장점도 있지만 단점도 있다. 다음 글에서 이러한 느슨한 결합에 어떤 장점과 단점이 있는지 알아보자.



반응형
저작자표시 비영리 변경금지
  1. 의존성이란?
  2. 강한 결합을 통해 이해하는 의존성
  3. 강한 결합이란?
  4. 강한 결합의 한계
  5. 느슨한 결합을 통해 강한 결합의 문제 해결하기


 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com

'Spring/Dependency Injection' 카테고리의 다른 글
  • [Spring] IOC Container에 등록된 모든 Bean의 이름 가져와 출력하는 방법 알아보기
  • [Spring] IOC Container 만들고 Bean 등록한 후 가져오는 방법 알아보기
  • [Spring] 의존성 주입이란 무엇인가? IOC Container과 Bean으로 알아보는 의존성 주입
  • [Kotlin] 느슨한 결합의 장점과 한계 자세히 알아보기
Dev.Cho
Dev.Cho
'조세영의 Kotlin World'는 Kotlin를 전문적으로 다루는 개인 기술 블로그입니다. Kotlin 세계에 대한 양질의 자료를 제공하며 Kotlin, Android, Spring, CI, CD 분야에 대해 다룹니다.
Dev.Cho
조세영의 Kotlin World
Dev.Cho
전체
오늘
어제

블로그 메뉴

  • LinkedIn
  • GitHub
  • 분류 전체보기 (491)
    • Kotlin (104)
      • Class and Interface (19)
      • Variable and Function (8)
      • Modifier (5)
      • Collection (14)
      • Time (8)
      • 동시성 제어 (7)
      • Reactive Programming (2)
      • Paradigm (2)
      • Kotlin 자료구조 (15)
      • Design Patterns (11)
      • Algorithm (3)
      • Exception (1)
      • 기타 (9)
      • Update History (0)
    • Coroutines (32)
      • Coroutine Basics (18)
      • Flow (9)
      • CoroutineScope (3)
      • Debugging (2)
    • Testing Codes (28)
      • Test 기본 (3)
      • JUnit5 (9)
      • MockK (6)
      • Testing Coroutines (1)
      • Testing Android (8)
      • Test 기타 (1)
    • Spring (50)
      • Dependency Injection (18)
      • Settings (5)
      • REST API (0)
      • DevTools (1)
      • MVC (18)
      • Error (2)
      • MongoDB (2)
      • Database (4)
    • Android (39)
      • Architecture (2)
      • Component (5)
      • Manifest (1)
      • Lifecycle (2)
      • Dependency Injection (17)
      • Resource (1)
      • Storage (1)
      • Security and Optimization (1)
      • WebView (2)
      • Error (6)
    • Android Jetpack Compose (33)
      • Compose (6)
      • Compose Modifier (13)
      • Compose Resource (4)
      • Compose State (4)
      • Compose Side Effect (6)
    • Android Jetpack Compose UI (48)
      • Compose Layout (14)
      • Compose Text (10)
      • Compose Button (5)
      • Compose Dialog (2)
      • Compose TextField (0)
      • Compose UIs (4)
      • Compose Animation (1)
      • Compose Canvas (12)
    • Android Jetpack (10)
      • Datastore (5)
      • ViewModel (4)
      • LiveData (1)
      • Paging (0)
    • KMP (5)
    • Programming (4)
    • Machine (9)
      • JVM (7)
      • Linux (2)
    • CI, CD (74)
      • Gradle (12)
      • Groovy Gradle (5)
      • Git (25)
      • Git Remote (5)
      • GitHub (5)
      • GitHub Actions (21)
    • Network (33)
      • GraphQL (12)
      • HTTP (11)
      • Basic (9)
    • 오픈소스 (3)
    • Database (3)
      • MongoDB (3)
    • IDE (6)
      • Android Studio (2)
      • Intellij (4)
    • Firebase (1)
    • Javascript (9)

공지사항

  • 코틀린 코루틴 완전 정복 강의 in 인프런 오픈
  • 코틀린 코루틴의 정석 책 출간
  • Kotlin Coroutines 공식 기술 문서 한국어 번⋯
  • GitHub에서 조세영의 Kotlin World를 Foll⋯
  • 문의&제안

인기 글

태그

  • Kotlin
  • Class
  • 유닛 테스팅
  • github actions
  • GraphQL
  • junit5
  • flow
  • java
  • GIT
  • 의존성 주입
  • gradle
  • compose
  • 안드로이드
  • Android
  • github
  • junit4
  • 코루틴
  • 코틀린
  • 스프링
  • Spring
  • junit
  • HTTP
  • Unit Testing
  • Spring boot
  • kotlin spring
  • Coroutine
  • Dependency Injection
  • Android Compose
  • dagger2
  • Jetpack Compose

최근 글

반응형
hELLO · Designed By 정상우.v4.3.0
Dev.Cho
[Kotlin] 의존성이란 무엇인가? 강한 결합과 느슨한 결합을 통해 이해하는 의존성
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.