일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 부꾸
- 부꾸러미
- 구슬
- 눈송이
- Spring
- redis
- 북극곰
- jscode
- 사이드 프로젝트
- bean
- 테오의 스프린트
- 트러블슈팅
- 모의면접
- 동적 SQL
- 오블완
- 후기
- Database
- SQL
- 프로그래머스
- jooq
- Java
- dto projection
- 글또
- spring context
- 코드트리
- 글또 #다짐
- open contribution jam
- 체험
- 티스토리챌린지
- 보따리
- Today
- Total
벤티의 개발 로그
[디자인 패턴] '전략 패턴'이 뭔지 아세요? 본문
"전략 패턴이 뭔지 아세요?"
머릿속이 새하얘졌다. 이 질문이 들어오기 전까지 모든 질문들에 자신 있게 대답했었기 때문일까? 3년 전에 <헤드 퍼스트 디자인 패턴> 스터디를 참여해 완주했지만, 바로 기억나지 않았다. 결국 나는 답을 하지 못했고, 이 질문을 제외하고 다른 질문에는 모두 답을 했으나, 결과적으로 불합격했다. 그래서 이번 글을 통해 전략 패턴에 대해 정리해 봤다.
전략 패턴이란?
전략 패턴은 '객체의 행동'을 바꿀 수 있게 해주는 디자인 패턴이다.
가장 일반적인 예제인 게임 캐릭터를 예시로 들어보면 무슨 뜻인지 알 수 있다. 게임 캐릭터는 여러 가지 방법으로 공격을 할 수 있다. 물 속성 스킬을 쓸 수도 있고, 불 속성 스킬을 쓸 수도 있고... 하지만 이 모든 스킬에 대해서 클래스를 일일이 만들면 당연히, 중복 코드가 매우 많아질 것이다.
이때 전략 패턴이 이 문제를 해결해 줄 수 있다. 그 방법은 바로 '공격 방법'을 객체로 분리하는 것이다.
전략 패턴, 왜 쓸까?
우선, 전략 패턴은 크게 아래 3가지 요소로 이루어져 있다.
1. Context(문맥): 전략을 사용하는 쪽으로, 위 예제에서는 '게임 캐릭터'이다.
2. Strategy(전략): 전략이 어떤 행동을 할지에 대한 약속으로, 위 예제에서는 '공격'이다.
3. Concrete Strategy(구체 전략): 실제로 어떻게 동작할지에 대한 설명으로, 위 예제에서는 '물 속성, '불 속성' 등이 해당한다.
설명을 다시 읽어봤더니, Java의 interface와 유사했다. Context를 Service 객체라고 한다면, Service 클래스에 있는 코드가 Strategy이고, ServiceImpl 클래스에 있는 코드가 Concrete Strategy인 것이다.
여기까지 깨달았더니, 전략 패턴을 쓰는 이유는 파악하기가 쉬웠다.
우선 '물 속성'과 '불 속성'을 각각 하나의 '전략'으로 관리한다고 하면, 게임 캐릭터 쪽 코드에서 할 일은 그저 사용자의 요청에 따라 각 속성의 메서드를 호출하는 것이다. 다시 말해, 객체의 입장에서 '전략'을 쉽게 교체할 수 있다.
그뿐만 아니라, 각 스킬의 속성을 독립적으로 관리할 수 있다. 예를 들어, 불 속성 스킬의 데미지 수치를 변경한다고 하면, 불 속성을 구현한 메서드의 코드만 수정하면 된다.
또한, '확장성' 측면에서도 매우 유리한 패턴이다. 예를 들어 '전기 속성' 스킬을 추가해야 할 경우, 전략 패턴을 사용했다면 각 속성의 interface에 선언된 메서드들을 오버라이딩하여 구현만 하면 된다.
그리고 가장 큰 장점은 역시, 가독성이다. 이렇게 코드를 관리하면 중복 코드를 크게 줄일 수 있다.
실무에서는 어떻게 쓰일까?
사실 앞선 예제는 아주 간단하기에, 실무에 직접적으로 쓰기에는 무리가 있다. 그래서 다른 예제는 없을까 고민한 끝에 사실 나는 이미 전략 패턴을 이미 사용하고 있다는 것을 깨달았다.
실제로 아주 많은 예제가 있지만, 가장 간단한 예제는 예전에 개발한 사이드 프로젝트에서 가져온 아래 사진 한 장으로 대체하겠다.
바로 소셜 로그인이다. 사용자(=객체)의 입장에서는 어떤 방법(=구체 전략)을 사용해서 로그인(=전략)할지만 정하면 된다.
이 내용을 코드로 정리하면 아래와 같다.
// Strategy 클래스
public interface OAuthLogin {
void login(String authCode);
String getProvider();
}
이 클래스는 Strategy 클래스로, '소셜 로그인'이라는 '전략(행동)'에 대한 클래스이다. 이 클래스에는 소셜 로그인에 해당하는 login() 메서드와, 어떤 소셜 로그인인지 구분하는 getProvider() 메서드가 있다.
// 네이버 로그인
@Component
public class NaverLogin implements OAuthLogin {
@Override
public void login(String authCode) {
// 생략
System.out.println("네이버 OAuth로 로그인 진행!!!");
}
@Override
public String getProvider() {
return "naver";
}
}
// 카카오 로그인
@Component
public class KakaoLogin implements OAuthLogin {
@Override
public void login(String authCode) {
// 생략
System.out.println("카카오 OAuth로 로그인 진행!!!");
}
@Override
public String getProvider() {
return "kakao";
}
}
이 두 클래스(Component)는 각각 네이버 로그인과 카카오 로그인에 대한 클래스, 즉 '구체 전략'에 해당하는 클래스로, 전략 패턴의 구조와 원리에 집중하기 위해 간단한 스켈레톤 코드처럼 정리해 봤다.
이렇게 코드를 짜면 Service 클래스에서 할 일은 사용자의 요청에 따라, 각 소셜 로그인 플랫폼에 알맞게 오버라이딩된 login() 메서드를 호출하는 것이다.
"전략 패턴이 뭔지 아세요?"
다시 첫 질문으로 되돌아가 보자. 만약 다시 그때 그 순간으로 되돌아갈 수 있다면 아래와 같이 답변할 것이다.
전략 패턴이란, 어떤 객체가 상황에 맞는 어떤 행동을 할 수 있게 도와주는 디자인 패턴입니다. 이를 코드를 작성할 때 적용하면, 쉽게 변경 사항을 반영할 수 있고, 독립적으로 관리할 수 있을 뿐만 아니라, 확장성에도 유리하고, 중복 코드를 줄일 수 있다는 장점이 있습니다.
결과적으로 면접에 합격하지는 못했지만, 덕분에 예전에 책을 읽고 마크다운 파일에 정리만 해뒀던 전략 패턴에 대해 좀 더 실무적으로 복습할 수 있는 좋은 기회가 되었다! 😊