요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

이번 글은 자바 디자인 패턴 시리즈의 마지막 편으로 행동 패턴(Behavior Patterns)에 대해 살펴보겠습니다. 행동 패턴은 이전 시리즈에서 살펴 본 구조 패턴(Structural Patterns), 생성 패턴(Creational Patterns)과 함께 디자인 패턴의 한 축을 담당하고 있는데요. 지금부터 실제 프로젝트에서 자주 활용되는 행동 패턴 종류를 알아보고, 프로젝트 적용 방법에 대해 살펴보겠습니다.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

자바 행동 패턴은 어떻게 사용해야 할까?

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

이번 글은 자바 디자인 패턴 시리즈의 마지막 편으로 행동 패턴(Behavior Patterns)에 대해 살펴보겠습니다. 행동 패턴은 이전 시리즈에서 살펴 본 구조 패턴(Structural Patterns), 생성 패턴(Creational Patterns)과 함께 디자인 패턴의 한 축을 담당하고 있는데요. 지금부터 실제 프로젝트에서 자주 활용되는 행동 패턴 종류를 알아보고, 프로젝트 적용 방법에 대해 살펴보겠습니다.

 

행동 패턴이란?

1) 개념 및 특징

행동 패턴은 객체의 상호작용책임 분산에 초점을 둔 디자인 패턴을 말합니다. 시스템 설계 시 행동 패턴을 적용하면 복잡한 객체 간 통신을 간소화하고, 각 객체의 독립성을 유지할 수 있습니다. 또한 객체를 독립적으로 다룰 수 있게 되어 테스트를 편리하게 진행할 수 있으며, 코드의 재사용성도 높일 수 있습니다.

 

<출처: 작가>

 

2) 행동 패턴의 종류

일반적으로 사용되는 행동 패턴의 종류는 약 10가지 정도가 있습니다. 이번 글에서는 이 중에서 실제 프로젝트에서 자주 사용되는 커맨드 패턴(Command Pattern), 옵저버 패턴(Observer Pattern), 스테이트 패턴(State Pattern)을 살펴보려고 합니다.

 

각 패턴을 간단히 요약하면, 커맨드 패턴은 명령 또는 요청 자체를 객체로 캡슐화한 패턴을 말하며, 옵저버 패턴은 객체의 상태 변경을 다른 객체들에게 전파하는 패턴을 말합니다. 스테이트 패턴은 객체의 내부 상태에 따라 해당 객체의 행동을 변경하는 패턴입니다.

 

 

커맨드 패턴(Command Pattern)이란?

1) 개념 및 특징

커맨드 패턴은 명령(Command)을 객체로 캡슐화하고, 이를 전달하는 호출자와 명령을 처리하는 수행자로 클래스를 분리하는 패턴을 말합니다. 시스템 설계에 커맨드 패턴을 적용하면 명령을 저장하여 나중에 실행하거나, 취소하고 다시 실행하는 등의 조작이 가능하여 보다 유연한 코드를 짤 수 있게 됩니다.

 

2) 커맨드 패턴 사용 예시

커맨드 패턴 예시로 다용도 리모컨을 구현한 코드를 만들어 보도록 하겠습니다. 우선, 다음과 같이 모든 커맨드 클래스가 구현해야 하는 Command 인터페이스를 정의합니다. 그러면 Command 인터페이스의 execute() 메서드를 오버라이딩(overriding)하여, 다양한 종류의 Command 클래스를 만들 수 있습니다. 이 예제에서는 execute() 메서드에 전등을 켜는 명령(Turn on)을 넣어 LightCommand를 생성했습니다.

.

<출처: 작가>

 

다음으로 실제 전등을 의미하는 Light 클래스를 만들고, 리모컨을 나타내는 RemoteControl 클래스를 만듭니다. 여기서 Ligth 클래스는 수신자(Receiver) 클래스로 명령(command)을 받아 실제 작업을 처리하는 객체가 됩니다. RemoteControl 클래스는 호출자(Invoker) 클래스로서 클라이언트의 요청을 받아 수신자 클래스에 명령을 전달하는 역할을 합니다.

 

<출처: 작가>

 

클라이언트 코드에서는 호출자인 RemoteControl 객체에 LightOnCommand를 설정한 후 해당 커맨드를 실행합니다. 즉, 리모컨 버튼을 누르면 전등의 불이 켜지게 됩니다.

 

<출처: 작가>

 

이번에는 전등을 켜는 리모컨을 TV를 켜는 리모컨으로 바꿔보겠습니다. 먼저 아래 코드와 같이 다른 수신자 클래스인 TV 클래스를 만듭니다. 그리고 Command 인터페이스의 execute() 메서드를 오버라이딩하여, LightCommand와 유사하게 TVCommand 클래스를 생성합니다.

 

<출처: 작가>

 

이제 아래 코드처럼 이전에는 전등용으로 사용하던 리모컨을 클라이언트 단에서 TV용 리모컨으로 바꿀 수 있습니다. 이처럼 커맨드 패턴을 이용하면, 호출자 객체에 다양한 커맨드를 설정하여 부품을 갈아끼듯이 사용할 수 있습니다. 이를 통해 호출자와 수신자 객체 간의 의존성을 줄이고, 각 코드의 재사용성을 높일 수 있는 것이죠.

 

<출처: 작가>

 

 

옵저버 패턴(Observer Pattern)이란?

1) 개념 및 특징

옵저버 패턴은 객체의 상태나 데이터가 변경될 때마다 다른 객체에 알림을 보내는 패턴을 말합니다. 주로 이벤트 기반 아키텍처(Even Driven Architecture)에 활용되며, 이벤트 처리 시 객체 간의 결합도를 낮춰 독립적으로 동작할 수 있게 하는 장점이 있습니다. 옵저버 패턴은 Subject와 Observer 인터페이스 그리고 각 인터페이스를 구현한 클래스들로 구성되는데요. Subject는 상태나 데이터가 변경될 때 Observer에 알려주는 역할을 하며, Observer는 Subject의 변경을 관찰하고 알림을 받는 대상이 됩니다.

 

2) 옵저버 패턴 사용 예시

옵저버 패턴의 예시로 날씨 정보 디스플레이를 구현해 보겠습니다. 먼저, 옵저버 패턴을 적용하기 위해서 다음과 같이 SubjectObserver 인터페이스를 정의합니다. 그리고 Subject 인터페이스에서는 옵저버를 관리하고 알림을 보내는 메서드들을 정의하고, Observer 인터페이스에서는 Subject로부터 받은 정보를 업데이트하는 update() 메서드를 정의합니다.

 

<출처: 작가>

 

다음으로 기상 데이터(온도와 습도)를 관리하고, 변경된 정보를 옵저버에 알리는 WeatherStation 클래스를 만듭니다. WeatherStation 클래스는 Subject 인터페이스를 구현하여 옵저버를 등록하거나 삭제할 수 있습니다. 또한 기상 데이터가 업데이트되면, 옵저버 리스트에 있는 각 옵저버에 변경된 정보를 통보하게 됩니다.

 

 

이제 WeatherStation의 업데이트된 기상 정보를 받아 디스플레이에 출력하는 WeatherScreen이라는 클래스를 만듭니다. WeatherScreen 클래스는 Observer 인터페이스의 update() 메서드를 오버라이딩합니다.

 

 

클라이언트 코드에서는 아래와 같이 WeatherScreen 객체를 생성하고, 그 객체를 WeatherStation 옵저버 리스트에 등록합니다. 그러면 이제 WeatherStation에서 기상 데이터가 변경될 때마다 WeatherScreen의 데이터도 같이 변경되는 것을 볼 수 있습니다.

 

<출처: 작가>

 

마지막으로 WeatherScreen 객체를 WeatherStation 옵저버 리스트에서 제거한 후, 다시 display() 메서드를 호출해 봅니다. 그러면 WeatherScreen은 WeatherStation의 옵저버 리스트에 없기 때문에, 업데이트된 정보를 더 이상 받지 않으며 이전에 표시된 값이 출력됩니다. 이처럼 옵저버 패턴을 적용하면 특정 객체의 업데이트 정보를 선택적으로 어떤 객체에게 전달할지 효과적으로 관리할 수 있습니다.

 

 

스테이트 패턴(State Pattern)이란?

1) 개념 및 특징

스테이트 패턴은 객체의 상태에 따라 동적으로 행동을 변경할 수 있게 해주는 디자인 패턴입니다. 스테이트 패턴을 적용하면 if나 switch문으로 지저분한 분기 코드를 만드는 것보다, 코드의 가독성을 향상시키고 확장성을 높일 수 있습니다. 또한 각 상태가 자신의 행동을 캡슐화함으로써 단일 책임 원칙(Single Responsibility Principle)을 준수하고, 코드 유지 보수를 용이하게 하는 장점이 있습니다.

 

2) 스테이트 패턴 사용 예시

스테이트 패턴의 예시로 사용자의 로그인 상태에 따라 다르게 동작하는 코드를 작성해 보겠습니다. 먼저 스테이트 패턴을 적용하기 위해 UserState 인터페이스를 만듭니다. UserState 인터페이스에서는 사용자의 상태에 따른 login()과 logout() 메서드를 정의하는데요. 이 메서드들은 사용자 상태 변화에 따라 다르게 동작하는 메서드들입니다.

 

<출처: 작가>

 

여기서 LoggedInState 클래스와 LoggedOutState 클래스는 UserState 인터페이스를 구현한 구체적인 상태  클래스(Concrete State Class)입니다. 이 클래스들을 통해 User의 구체적인 상태들을 구분하고 각 상태에서의 동작을 구현합니다.

 

<출처: 작가>

 

이어서 UserContext 클래스를 구현합니다. 이 클래스는 사용자의 현재 상태를 추적하며, 상태 변화 시 setState() 메서드를 이용해 해당 상태를 업데이트합니다. 또한 사용자의 로그인 상태에 따라 login()과 logout() 메서드가 다르게 동작하도록 정의하죠.

 

<출처: 작가>

 

클라이언트 코드에서는 UserContext 객체를 생성하고, 이 객체를 통해 로그인과 로그아웃 요청을 합니다. 그러면 UserContext의 setState() 메서드를 통해 로그인 상태가 변경되고, login()과 logout() 메서드를 통해 현 상태에 따른 동작을 수행하게 됩니다.

 

<출처: 작가>

 

이처럼 스테이트 패턴을 적용하면, 클라이언트는 User의 구체적인 상태를 몰라도 UserContext만 호출하면 됩니다. 즉, 자동으로 현재 로그인 상태에 맞는 동작을 실행하게 되는 것이죠. 결과적으로 클라이언트 코드와 User의 상태 관리 코드는 서로 분리되어 코드의 유지 보수가 더욱 간편해지게 됩니다.

 

 

실제 프로젝트에서의 적용 방법은?

1) 커맨드 패턴

커맨드 패턴은 다양한 명령의 실행 및 취소, 로깅, 트랜잭션 등의 기능이 필요할 때 적용을 고려해 볼 수 있습니다. 특히 복잡한 호출 구조를 갖는 여러 작업을 추상화하고 캡슐화해야 할 때 커맨드 패턴이 유용한데요. 이 밖에도 시스템의 명령을 대기열에 저장하거나, 로직의 요청자와 수행자를 분리해야 하는 경우에도 커맨드 패턴을 적용하는 것이 좋습니다.

 

다만 커맨드 패턴 사용하면 각 명령에 대한 클래스를 개별적으로 생성해야 하므로 클래스의 수가 급격히 늘어날 수 있습니다. 이로 인해 코드가 복잡해지고, 프로젝트에 새로 투입된 개발자가 이해하기 어려운 코드가 될 수도 있습니다. 따라서 커맨드 패턴을 적용할 때는 해당 패턴이 적용된 범위를 잘 파악하고 관리하는 것이 중요하며, 무분별하게 패턴을  남용하지 않도록 해야 합니다.

 

2) 옵저버 패턴

프로젝트에서 한 객체의 상태 변화가 다른 객체들에게 영향을 주는 경우 옵저버 패턴의 적용을 고려해 볼 수 있습니다. 특히 상태 변화에 따른 알림이나 업데이트를 자동화하고자 할 때 옵저버 패턴이 매우 유용한데요. 동적으로 관찰자를 추가하거나 제거해야 하는 상황에서도 옵저버 패턴은 효과적인 디자인 패턴입니다.

 

하지만 Subject 클래스에 등록된 옵저버가 과도하게 많아지면, 알림을 보내는 과정에서 성능 문제가 발생할 수도 있습니다. 또한 옵저버 패턴 적용 시 잘못된 설계나 구현의 실수로 클래스 간의 순환 의존성이 발생하는 경우도 있기 때문에 주의가 필요합니다.

 

3) 스테이트 패턴

스테이트 패턴은 객체의 내부 상태에 따라 동작이 변경되어야 하는 경우 적용하기 좋습니다. 특히, if나 switch문으로 3가지 이상의 상태를 분기해서 처리하던 코드를 리팩토링(refactoring) 할 때도 유용하게 사용할 수 있는데요. 객체의 상태 변화 로직을 명확히 분리하고, 상태 관리를 조금 더 직관적으로 할 수 있기 때문입니다.

 

다만 스테이트 패턴도 커맨드 패턴과 마찬가지로 각 상태에 따른 동작을 별도의 클래스로 분리해야 하기 때문에, 클래스의 수가 과도하게 증가할 수 있습니다. 이로 인해 전체 시스템의 복잡도가 증가하고 코드의 가독성이 떨어질 수 있습니다. 따라서 패턴을 적용하기 전에 스테이트 패턴의 이점이 그에 따르는 복잡도와 비용을 상쇄하는지 따져봐야 합니다.

 

 

마치며

이번 글에서는 자바 디자인 패턴 시리즈의 마지막으로 행동 패턴의 종류와 주요 특성, 그리고 실제 프로젝트에서의 적용 방법에 대해서 살펴봤습니다. 특히 이번에 다룬 커맨드 패턴, 옵저버 패턴, 스테이트 패턴은 실무에서도 흔히 활용되기 때문에 익혀두는 것이 좋습니다. 아울러 이외에도 전략 패턴(Strategy Pattern)과 템플릿 메소드 패턴(Template Method Pattern)도 자주 쓰이는 행동 패턴이니 따로 학습해두는 것을 권장합니다.

 

지금까지 자바 디자인 패턴 시리즈로 총 4편의 글을 정리했습니다. 어댑터 패턴을 시작으로 자바 디자인 패턴에 대해 알아보고, 이후 2,3,4편에서는 디자인 패턴의 세 축인 구조 패턴, 생성 패턴, 행동 패턴에 대해서 각 패턴의 종류와 프로젝트 적용 방법을 살펴보았습니다.

 

디자인 패턴은 코드 가독성을 높이고 유지 보수를 용이하게 할 수 있기 때문에 개발자로서 꼭 공부해두는 것이 좋습니다. 다만 디자인 패턴은 그 자체로 100% 완전무결한 것이 아니므로, 각 패턴의 장단점과 프로젝트 적용 시 주의할 점들도 함께 알아두길 바랍니다.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
Developer, Blogger
214
명 알림 받는 중

작가 홈

Developer, Blogger
214
명 알림 받는 중
곰씨네 IT를 비롯하여 다양한 블로그를 운영 중인 개발자입니다. 2010년부터 LG CNS에서 소프트웨어 엔지니어로 근무하며, LG전자 물류 시스템 구축, 스마트 TV OS 개발, LG화학 모바일 프로젝트 등에 참여했습니다. 2017년 미국으로 이주해 프리랜서 개발자로 전향했으며, 현재는 AI와 머신러닝 분야로의 경력 확장을 위해 미국 매사추세츠 주립대에서 컴퓨터 공학 석사 과정을 병행하고 있습니다.

운영 중인 블로그
곰씨네 IT: https://gomcine.tistory.com
곰씨네USA: https://gomcineusa.tistory.com
코리얼티USA: https://korealtyusa.com

저서
개발자가 영어도 잘해야 하나요? (English for Developer)
http://gilbut.co/c/24026188iO

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기