아키텍쳐 기초
수많은 아키텍쳐가 있지만 학부생들 대부분은 원시 코드를 사용한다고 지적해 주셨다.
그 이유에 대해 코딩 테스트를 비판하시며 시스템을 크게 가져가면서 넓게, 크게 봐야 하는데 그걸 테스트 하지 않기 때문이라고 하셨다. 특히 아키텍쳐에 대한 생각 없이 개발하는 사람들이 대체 1순위이다,, 라고 하셨다. 되게 공감되는 이야기이였다. 요즘 채용 프로세스에 과제가 포함되는게 좋은 점인 것 같다.
추가로 링크드인을 꼭 가입하라고 하셨다. 나도 링크드인을 통해 modular monoliths를 접했고 현 프로젝트에 접목하고 있는데 정말 이건 혁신이다. 브라운 형님 멋있다
관심있으신 분들은 보시길
https://www.youtube.com/watch?v=5OjqD-ow8GE&t=1s
레퍼런스가 많이 없어서 고생을 좀 했는데
없으면 내가 해야지!! 레퍼런스로 좋은 예시를 만들어볼 계획이다 ㅎㅎ
싱글톤 패턴 Singleton Pattern
3가지 포인트가 있다
1. 싱글톤 패턴으로 만들어질 객체(데이터베이스 커넥션)는 자기 자신 내부에 정적 멤버 변수로 존재
2. 생성자는 private이어야 한다
3. 또다른 정적 멤버 함수는 객체가 null일때 private 생성자로 객체를 생성하고 있다면 1번에 있는 인스턴스를 반환
코드상에서 1, 2, 3번
3번 메서드는 왜 정적 메서드일까? -> 객체가 없으면 멤버함수를 못쓰니깐
반복자 패턴 Iterator pattern
c++ 할 때 standard template library로 많이 사용한 패턴이라고 한다.
집합 클래스의 자료구조와 상관없이 집합 소속 요소들에 쉽게 접근할 수 있도록 반복자에게 위임하는 구조.
클라이언트는 데이터 접근 방법을 신경쓰지 않아도 된다.
어댑터 패턴 Adapter pattern
사용 가능한 서비스의 인터페이스를 클라이언트가 예상하는 인터페이스에 맞게 조정
여기서 어댑터는
서비스가 제공하는 인터페이스를 클라언트가 기대하는 인터페이스로 조정한다.
예를 들어, 직접 제어하기 어려운 코드 특히 애널리틱 라이브러리가 저체적으로 XML을 JSON으로 변환할 수 있도록 애널러틱 라이브러리를 고침??
즉 최대한 코어 어플리케이션이나 권한이 없는 애널리틱 라이브러리를 건들지 않고 중간에 어댑터를 만들어 변환하라는 뜻
파사드 패턴 Facade Pattern
새로운 인터페이스를 만들어 감싸는 점이 어댑터 패턴과 동일하지만
그 안에 세부 함수들이 존재할때 절차에 신경쓰지 않고 클라이언트가 신경쓰지 않아도 된다는 점이다.
즉 그 세부 함수들을 또 하나의 인터페이스로 묶어주는 것이라고 하셨다
예를 들어 세탁기를 사용하는데
급수, 탈수, 세재, 헹굼 단계 -> 인터페이스로 존재하는데
이 모든걸 합친 인터페이스로 "세탁"이라는 인터페이스를 만드는 것이다.
클라이언트는 세부적인 서비스와 절차를 신경쓸 필요가 없다.
프록시 패턴 Proxy Pattern
프록시라는게 참 많이 보이는데
"대리"라는 뜻이다.
그러니까 아키텍쳐에서 프록시가 나오면 앞에 하나의 서버를 더 두고
로직을 분리해주는 느낌이 많다.
교수님의 예시는
캐싱, 접근제한, 로드밸런싱, SSL 등을 처리하는 프록시서버이다.
백엔드의 Tomcat WAS로 생각하면
우리가 만든 서버는 비즈니스 로직만을 처리하고
앞에 놓은 프록시서버는 캐싱, 접근제한, 로드밸런싱을 담당하는 것
AWS로 치면 로드밸런싱 제품으로 ELB (ALB) -> 접근제한, SSL 포함
캐싱으로 Cloud Front
요런게 있을 것 같다
데코레이터 패턴 Decorator Pattern
!!여기 좀 중요한 것 같다!!
집합 관계와 위임을 통해 기존 클래스의 동작을 가볍고 유연하게 확장하는 패턴이다.
흐름대로 가보면
- 기본적인 코드 수정은 OCP 위배
- 그럼 상속을 사용하자 -> 부모 자식간 컴파일 타임 의존 관계 발생, 마음대로 추가 삭제가 불가능하다
- 이를 해결하기 위해 만들어진게 데코레이터 패턴
원리를 알기 위해
먼저 재귀적 합성을 살펴보자
데코레이터 패턴에는
- Component (인터페이스)
- Component1 (구현체)
- 데코레이터 (확장 기능이 담김)
여기서 장식을 하는 대상이 누군지를 알아야 하는데
Component 인터페이스인거다.
Decorator는 재귀적 합성으로 addBehavior로 추가를 하는거다.
즉 Component 인터페이스의 구현체의 객체를 Decorator로 감싸주는것
정리를 한번 더 하면 Decorator는 추상적인걸 꾸며준다.
구현체로 만든 객체를 인터페이스로 받아주고
그 객체를 데코레어티로 감싸 추가적인 동작을 실행한다
코드로 보는게 명확하다
interface Component {
void operation();
}
class Component1 implements Component {
public void operation() {
}
}
abstract class Decorator implements Component {
Component wrappee; // 원본 객체를 composition
Decorator(Component component) {
this.wrappee = component;
}
public void operation() {
wrappee.operation(); // 위임
}
}
class Decorator1 extends Decorator {
Decorator1(Component component) {
super(component);
}
public void operation() {
super.operation(); // 원본 객체를 상위 클래스의 위임을 통해 실행하고
extraOperation(); // 장식 클래스만의 메소드를 실행한다.
}
void extraOperation() {
}
}
class Decorator2 extends Decorator {
Decorator2(Component component) {
super(component);
}
public void operation() {
super.operation();
extraOperation();
}
void extraOperation() {
}
}
실행
Component obj = new Component1();
Component deco1 = new Decorator1(obj);
deco1.operation();
Component deco2 = new Decorator2(obj);
deco2.operation();
Component deco3 = new Decorator1(new Decorator2(obj));
deco3.operation();
예시로 Notifier 알림을 개발할때
3가지 데코레이가 존재
Notifier는 interface겠지?
이걸 사용한다고 했을때
BaseNotifier를 만들어 주고 -> 기본 동작을 수행하는 녀석 (구현체)
Notifier notifier = new NotifierImpl();
SMS를 사용할때는
Notifier smsDecorated = new SmsDecorator(notifier);
smsDecorated.send();
FaceBook을 사용할때는
Notifier facebookDecorated = new FaceBookDecorator(notifier);
facebookDecorated.send();
SMS + FaceBook 사용할때는
Nottifier smsFacebookDecorated = new FaceBookDecorator( new SmsDecorator(notifier));
smsFacebookDecorated.send()
이런식일거다.
<------------->
주의! 밑에 그림과 내가 쓴 코드 네이밍이 다르다
내가 NotifierImpl이라고 한 부분이 BaseDecorator
하지만 주의해야할점
합성되는 순서에 따라 로직이 결정된다
-> 그러므로 순서가 중요하고, 순서 상관없이 수행하고 싶다면 데코레이터 패턴을 사용하면 안된다
Nottifier smsFacebookDecorated = new FaceBookDecorator( new SmsDecorator(notifier));
내부 구현은
super();
operate();
메서드에서 이 순서로 구현하고 send를 호출한다면
Base operation -> SMS operation -> Facebook operation 순서대로 수행 될 것
만약 이 순서를 원하지 않는다면 뒤에 나오는
팩토리 메서드 패턴 혹은 추상 팩터리 패턴을 사용하면 된다.
팩토리 메서드 패턴 Factory Method Pattern
클래스의 새로운 객체를 생성할때 사용된다.
객체를 생성하는 책임을 분리하는 패턴이다.
Dialog가 Button을 만들어내는 클래스인 상황에서 (팩토리)
다이얼로그 형태가 다를때마다 버튼, 렌더링 방식은 다를거다.
그렇기 때문에 여기서 사용하는 생성자를 분리하라는 뜻이다.
즉 Dialog가 사용하는 생성자를 다른 클래스로 분리
추상 팩토리 패턴 Abstract Factory Pattern
단일책임 원칙
SRP를 지키기 위해 추상 인터페이스로 객체 패밀리를 생성하는 것이다.
객체 패밀리를 만드는 점이
위에서 본 어댑터 -> 파사드 넘어갈때랑 굉장히 비슷한 것 같다.
예시 종합본
new라고 적어놓은 부분은
GUIFactory factory = new WinFactory();
GUIFactory factory = new MacFactory();
이렇게 사용한다는 뜻
상태 패턴 State Pattern
상태에 맞게 객체 동작을 변경해야 하는 경우에 사용한다.
Context가 가지고 있는 State 멤버 변수 구현체로 어떤걸 집어넣어 주는지에 따라
어떻게 동작을 하는지가 변경된다
교수님이 말하신 내용은 아니지만 개인적으로 이 패턴은
추상적인것에 의존해야한다인 -> DIP를 적용해야 하는 구체적인 상황에 대해 말한다고 느껴진다.
전략 패턴 Strategy Pattern
상태 패턴과 비슷하다
어떤 전략을 가지고 있는지에 따라 수행할 오퍼레이션을 달리한다.
차이점은?
상태 패턴은 스테이트 머신끼리 의존관계가 존재한다.
예를 들어, Draft -> Moderation -> Published 상태는 전 단계가 끝나야 진행되는 것이기 때문에
일종의 의존 관계가 존재한다
하지만 전략 패턴은 그냥 나열이다.
Road, PublicTransport, Walking 전략은 의존관계가 전혀 없다.
걸어가든, 지하철을 타든, 운전을 하든 그냥 그 전략에만 집중하면 된다.
단점으로는
- 전략이 소수일때는 과한 구조일 수 있다
관찰자 패턴 Observer Pattern
옵서버는 변경을 통지받고 접근을 요청하는 클래스이다.
구체적으로 데이터를 보관하고 있는 Subject가 그 데이터를 이용하는 옵서버와 효과적으로 통신하며 느슨하게 결합된 패턴이다.
즉 Subject - 옵서버 목록을 유지, 변경을 고지
Observer - 변경을 통지받고 접근을 요청
이벤트기반 통신 / 메시징 큐에서
publisher, subscriber랑 똑같은 것 같다
밑에 그림에서 Editor가 publisher
event listener들이 subscriber가 되겠다
역시 내가 좋아하는 교수님.,,
바로 publisher, subsriber에 대해 이야기를 해주셨다
채팅앱을 예시로 들어주셨는데
xmpp를 사용하는 오픈소스로 Jabber를 소개해 주셨다. 요건 생략
Composite Pattern
특정 클래스가 어떤 객체의 집합을 가지고 있는 패턴이다.
예를 들어
GUI 컴포넌트가 여러 위젯을 가지고 있으면 여러 컴포넌트를 가지고 있는다고 하는데 이는 컨테이너라고도 부른다.
Memento Pattern
컴포넌트의 여러 상태를 저장하고 복원할 수 있는 패턴.
AOP
aspect oriented programming
내가 좋아하는 관점 지향이 수업 중간에 잠깐 나왔다
기존 OOP에서 여러 클래스에 넘나드는 공통적인 기능을 활성화 할떄 aop를 사용한다.
세오스 스터디를 진행하며 이걸 구체적으로 정리한 적이 있는데 깃허브 리드미에만 올려서... 나중에 블로그에도 옮겨야겠다
AOP를 사용하는 대표적인 예시로 로깅이 있다.
아키텍쳐 평가
아키텍쳐나 디자인 패턴의 속성, 강점 및 약점을 결정하는 방법이다.
SAAM
software architecture analysis method
시나리오 기반 평가 방법
여러가지 시나리오를 가지고 실행이 되는지 확인하는것
즉 테스트케이스를 여러가지 경우로 실행해보는것
ATAM
architecture trade-off analysis method
여러가지 품질 속성에 초점을 맞춰 평가하는 방법
그후 업무 배경 및 요구 사항을 본 다음에 품질 속성을 뽑아낸다
이에 맞춰서 시나리오를 작성하고 그에 따른 아키텍쳐를 만들어 접근법을 본 후 의사결정을 내린다.
즉, 반복적으로 아키텍쳐를 적용해봄 -> 아키텍쳐간의 trade off를 비교해봄
SAAM과 다른 점으로 품질속성이 추가되어 있다는 점이 있다.
정리
배운 디자인 패턴을 정리하면
- 싱글톤 패턴 Singleton
인스턴스를 하나만
- 반복자 패턴 Iterator
인터페이스로 순회
- 어댑터 패턴 Adaptor
interface로 감싸기, 사용자에게 친숙한 인터페이스를 제공
- 파사드 패턴 Facade
어댑터에서 순서를 묶어서 제공 EX) 세탁기
- 프록시 패턴 Proxy
앞에 대행자를 추가 EX) SSL, 로드밸런싱
- 팩토리 메서드 패턴 Factory method
생성자를 분리
- 추상 팩토리 패턴 Abstract Factory
interface로 객체 패밀리를 생성
- 상태 패턴 State
경우의수를 interface로 관리
- 전략 패턴 Strategy
스테이트랑 유사한데 전략끼리의 의존성, 연관관계가 없음
- 관찰자 Observer
Publisher Subscriber
- Composite Pattern
집합을 가지고 있음
- Memento Pattern
상태를 기억
평가 방법
- SAAM
- ATAM
SAAM과 다른점은 품질 속성이 추가됨
'CS > Software Engineering' 카테고리의 다른 글
[소프트웨어 공학] 9. 코딩 (0) | 2025.06.06 |
---|---|
[소프트웨어 공학] 8. UI 설계 (0) | 2025.06.06 |
[소프트웨어 공학] 결합도와 응집도 + α (0) | 2025.06.06 |
[소프트웨어 공학] 6. 설계 원리 (0) | 2025.06.06 |
[소프트웨어 공학] 5-2. 요구 모델링 (0) | 2025.06.06 |