Study

[Swift OOP] SOLID - 쉽게 알아보는 ISP (인터페이스 분리 원칙)

차코.. 2024. 10. 3. 17:03

들어가며

 

객체 지향 설계에서 자주 언급되는 SOLID

프로그램의 유지보수성과 확장성을 높이기 위해 꼭 필요한 원칙인데요.

 

SOLID 원칙
- SRP(Single Responsibility Principle): 단일 책임 원칙
- OCP(Open Closed Priciple): 개방 폐쇄 원칙
- LSP(Liskov Substitution Priciple): 리스코프 치환 원칙
- ISP(Interface Segregation Principle): 인터페이스 분리 원칙
- DIP(Dependency Inversion Principle): 의존성 역전 원칙

 

 

그 중에서도 이번 글에서는 SOLID 원칙 중 네번째에 해당하는 인터페이스 분리 원칙(ISP)에 대해

그림과 Swift의 예시 코드로 설명해보겠습니다.

 

 


✅ ISP(인터페이스 분리 원칙)의 필요성

 

 

ISP에 대해 알아보기 전에, 또 오늘의 주인공 김아요 군을 모셔보겠습니다.

 

김아요 군이 처음으로 iOS 앱개발에 도전해본다고 하는데요,

 

 

앱을 개발하는 김아요 군

 

 

어떤 앱인지 모르겠지만 .. 

카메라, 음악, sns 기능이 모두 들어가 있는 엄청난 앱을 개발한다고 합니다.

 

김아요 군의 앱 코드를 살짝 훔쳐볼까요?

 

// 김아요 군의 앱 인터페이스
protocol SmartPhoneApp {
    func takePhoto()
    func playMusic()
    func postToSocialMedia()
}

// 카메라 기능 클래스
class Camera: SmartPhoneApp {
    func takePhoto() {
        print("사진을 찍습니다.")
    }
    
    func playMusic() {
        // 카메라 기능은 음악 기능이 필요하지 않음
        print("카메라 앱에서는 음악을 재생할 수 없습니다.")
    }
    
    func postToSocialMedia() {
        // 카메라 기능은 소셜 미디어 글쓰기 기능이 필요하지 않음
        print("카메라 앱에서는 소셜 미디어에 게시할 수 없습니다.")
    }
}

// 음악 기능 클래스
class MusicPlayer: SmartPhoneApp {
    func takePhoto() {
        // 음악 기능은 사진을 찍을 필요가 없음
        print("음악 플레이어 앱에서는 사진을 찍을 수 없습니다.")
    }
    
    func playMusic() {
        print("음악을 재생합니다.")
    }
    
    func postToSocialMedia() {
        // 음악 기능은 소셜 미디어 글쓰기 기능이 필요하지 않음
        print("음악 플레이어 앱에서는 소셜 미디어에 게시할 수 없습니다.")
    }
}

 

 

어음...

 

앱 내의 기능 클래스들이 따르는 SmartPhoneApp 프로토콜을 만들고,

그 프로토콜은 세부 기능들(사진 찍기, 음악 듣기, 포스팅하기)를 모두 갖고 있네요.

 

이 프로토콜을 따르는 Camera, MusicPlayer 클래스들의 구현부는 어떨까요?

 

카메라에게는 음악 듣기, 포스팅하기 기능이 불필요하고,

음악에게는 사진 찍기, 포스팅하기 기능이 불필요해 필요 없는 코드를 작성하게 되었네요.

 

 

ISP를 준수하지 않음

 

이는 김아요 군이 ISP(인터페이스 분리 원칙)를 지키지 않아서 발생한 일인데요,

 

인터페이스가 너무 거대해 각각의 기능들이 본래 역할 외의 불필요한 책임들을 갖게 되었습니다.

 

이는 유지보수성이 떨어지고, 코드의 복잡성을 증가시키는 요인이 됩니다.

 

 

어떻게 해결해 줄 수 있을까요?

 

 


✅ ISP(인터페이스 분리 원칙) 적용하기

 

 

ISP(인터페이스 분리 원칙)는 다음과 같은 규칙을 따르는데요,

 

- 클라이언트는 자신이 사용하지 않는 메서드에 의존해서는 안됩니다.
- 필요한 기능만 포함된 작은 인터페이스를 사용해야 합니다.

 

 

즉, 큰 인터페이스를 여러 개의 작은 인터페이스로 분리해주어

각 클래스가 자신이 가져야 하는 기능의 인터페이스만 구현하도록 설계해야 합니다.

 

 

김아요 군의 코드를 ISP에 맞추어 바꾸어 봅시다.

 

// 사진 촬영 기능
protocol PhotoTaking {
    func takePhoto()
}

// 음악 재생 기능
protocol MusicPlaying {
    func playMusic()
}

// 소셜 미디어 게시 기능
protocol SocialMediaPosting {
    func postToSocialMedia()
}

// 카메라 클래스 (사진 찍기 기능만 구현)
class Camera: PhotoTaking {
    func takePhoto() {
        print("사진을 찍습니다.")
    }
}

// 음악 플레이어 클래스 (음악 재생 기능만 구현)
class MusicPlaye: MusicPlaying {
    func playMusic() {
        print("음악을 재생합니다.")
    }
}

// SNS 클래스 (소셜 미디어 게시 기능만 구현)
class SocialMediaApp: SocialMediaPosting {
    func postToSocialMedia() {
        print("소셜 미디어에 게시합니다.")
    }
}

// 사용 예시
let camera = Camera()
camera.takePhoto()  // "사진을 찍습니다."

let music = MusicPlayer()
music.playMusic()  // "음악을 재생합니다."

let socialMedia = SocialMedia()
socialMedia.postToSocialMedia()  // "소셜 미디어에 게시합니다."

 

 

이렇게 ISP를 준수하니 각 기능이 자신에게 필요한 세부 기능만을 구현하게 되어,

불필요한 의존성이 제거되었네요.

 

장기적으로 보았을 때

각 기능이 분리되어 있어, 새로운 기능을 추가하거나 기존 기능을 변경할 때

다른 기능에 영향을 미치지 않아 유지 보수가 용이합니다.

 


마치며

 

이렇게 해서 SOLID의 원칙 중 ISP에 대해 알아보았는데요.

 

ISP 원칙을 지키면 인터페이스를 나누어,

각 클래스가 필요한 기능만 구현해

유지 보수와 확장성을 높일 수 있습니다.

 

감사합니다.