반응형
[Part - 1] 아키텍처(Architecture)
- 프로젝트에 참여하는 개발자들이 설계에 대해 공유하는 이해를 반영하는 주관적인 개념
- 아키텍처는 주관적 → 같은 아키텍처라고 한다고 해도 서로 다른 팀에서 말하는 것이 세부적으로 다를 수 있음
- 아키텍처를 변경하는 것은 매우 많은 비용이 든다. → 변경하기 어렵다.
- 이유: 모든 개발자들이 현재 아키텍처를 공유하고 있기 때문
- 아키텍처는 가급적 일찍 올바르게 결정해야 한다.
- 아키텍처는 관심사의 분리이다.
- 관심사의 분리는 서로 다르고 관련이 없는 책임들을 분리하는 것
레이어 아키텍처(Layered Architecture)
- 서로 다른 관심사를 분리한 아키텍처
- 유사한 것들을 한 레이어에 모아놓음으로써 그 레이어만 교체를 하면 전체 시스템을 다른 환경에서도 사용 가능
→ 유연성 & 재사용성 높아짐 - 레이어는 팀마다 다를 수 있음 → 세분화 가능
- 가장 중요한 레이어는 도메인(Domain)
- 얼마나 도메인을 시스템에 잘 반영했느냐가 결정
- 시스템의 성공 원인: 사용자들이 많이 써주기 때문
- 발을 딛고 있는 도메인을 최대한 가장 적절하게 반영한 시스템이어야지만 성공을 한다
- 도메인 레이어 설계하는 방법
- 절차 지향 : 트랜잭션 스크립트(Transaction Script)
→ 절차 지향적으로 개발하는 것이 대부분 - 객체 지향 : 도메인 모델(Domain Model)
- 절차 지향 : 트랜잭션 스크립트(Transaction Script)
[Part - 2] 영화 예매 도메인
도메인 개념
영화(Movie)
- 제목, 상영시간
- 영화는 메타 데이터라고 볼 수 있음
상영(Showing)
- 영화, 상영일시 등
- 고객이 실제로 예매하는 것은 영화가 아니라 상영
할인 정책(Discount)
- Amount Discount: 특정 금액을 할인
- Percent Discount: 현재 금액의 특정한 비율을 할인
할인 규칙(Rule)
- Sequence Rule: 10회 상영인 경우 할인
- Time Rule: 특정 요일 특정 시간인 경우 할인
예매(Reservation)
- 영화, 상영 정보, 금액, 인원 등
- 고객이 최종적으로 받을 정보
할인 정책 + 할인 규칙
- Discount는 하나만 사용 가능(없을 수도 있음)
- Rule은 여러 개 존재 가능
- Discount가 있으면 반드시 Rule이 존재
할인 적용
예매
- 결과적으로 예매라는 도메인이 반환
도메인 개념 또는 후보 객체
- 영화는 여러 개(0개 이상)의 상영을 가진다.
- 상영은 여러 개(0개 이상)의 예매를 가진다.
- 영화는 0 또는 1개의 할인 정책을 가진다.
- 할인 정책은 1개 이상의 규칙을 가진다.
도메인이란 개발할 대상이다.
시스템 상에서 가져야하는 부분, 즉 시스템에 넣는 부분을 도메인이라고 한다.
[Part - 3] 트랜잭션 스크립트(Transaction Script) - 절차지향
- 트랜잭션 스크립트는 절차적으로 짠다
- 내가 다뤄야하는 데이터와 이를 조작하는 프로세스가 따로 동작한다는 의미
- 따라서 데이터와 프로세스를 따로 고민한다
데이터 모델
에너믹 도메인 모델(Anemic Domain Model)
- 데이터 모델과 똑같은 클래스를 배치, getter와 setter 넣음
- 로직이 없음
- 테이블이 있고 테이블의 필드를 그대로 클래스의 속성으로 매핑한 클래스들을 여러개 만들어 둔 것을 에너믹 도메인 모델이라 함
데이터 접근 객체(DAO)
- 클래스에 해당하는 DAO를 배치(각 테이블에 필요한 DAO를 구현)
예매 처리 스크립트
- ReservationScript(일반적으로 ReservationService)를 만듦
- 데이터를 읽을 DAO들을 injection
절차적인 예매 로직
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
1) 데이터 베이스로부터 Movie, Showing, Rule 조회
2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
3) if(Rule이 존재하면) {
Discount를 읽어 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
4) Reservation을 생성 후 데이터베이스에 저장
}
- 대부분 위와 같이 절차적으로 구현
- 도메인이 복잡해지면 객체지향이 적합
1. 데이터베이스로부터 Movie, Showing, Rule 조회
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
// 1) 데이터 베이스로부터 Movie, Showing, Rule 조회
// 필요한 데이터를 모두 준비한다.
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
// ...
}
2. Showing에 적용할 수 있는 Rule이 존재하는지 확인
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
// 2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
Rule rule = findRule(showing, rules);
// ...
}
private Rule findRule(Showing showing, List<Rule> rules) {
for (Rule rule : rules) {
if (rule.isTimeOfDayRule()) { // 시간 할인 규칙
if (showing.isDayOfWeek(rule.getDayOfWeek()) &&
showing.isDurationBetween(rule.getStartTime(), rule.getEndTime())) {
return rule;
}
} else { // 횟수 할인 규칙
if (rule.getSequence() == showing.getSequence()) {
return rule;
}
}
}
}
3. Rule의 존재 여부
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
/*
3) if(Rule이 존재하면) {
Discount를 읽어 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
*/
Money fee = movie.getFee();
if (rule != null) { // Rule이 null이 아니면 할인
fee = calculateFee(movie);
}
// ...
}
private Money calculateFee(Movie movie) {
Discount discount = DiscountDAO.selectDiscount(movie.getId());
Money discountFee = Money.ZERO;
if (discount != null) {
if (discount.isAmountType()) {
discountFee = Money.wons(discount.getFee());
} else if (discount.isPercentType()) {
discountFee = movie.getFee().times(discount.getPercent());
}
}
return movie.getFee().minus(discountFee);
}
4. Reservation을 생성하여 데이터베이스에 저장
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) { // Rule이 null이 아니면 할인
fee = calculateFee(movie);
}
// 4) Reservation을 생성해 데이터베이스에 저장
Reservation result = makeReservation(customerId, showingId, audienceCount, fee);
reservationDAO.insert(result);
return result;
}
private Reservation makeReservation(int customerId, int showingId, int audienceCount, Money payment) {
Reservation result = new Reservation();
result.setCustomerId(customerId);
result.setShowingId(showingId);
result.setAudienceCount(audienceCount);
result.setFee(payment);
return result;
}
데이터를 가져오고, 로직을 실행하는 것이 서비스 하나에서 모두 수행된다.
수많은 if, else, then 로직을 포함한다.
중앙 집중식(Centralized) 제어 스타일
- 서비스에서 모든 Control이 다 일어나고 모든 것을 판단한다.
- 시퀀스 다이어그램을 그려봤을 때, 어떤 제어가 한쪽으로 다 몰린다고 하면 트랜잭션 스크립트라고 보면 된다.
→ 절차지향적 시퀀스 다이어그램
[Part - 4] 도메인 모델(Domain Model)
- 프로세스와 데이터를 하나의 덩어리로 묶어서 생각한다.
- 객체지향 기반의 도메인 레이어 설계
- 객체지향은 협력하는 객체들의 공동체이다.
- 객체들이 메세지를 주고 받으면서 어플리케이션을 만들기 위해 협력하는 과정을 만들어간다.
- 객체지향적으로 설계한다는 것은 역할을 나누고 책임을 부여한다. 그리고 여러 객체들이 협력하여 상호작용한다.
객체지향 설계
- 객체 하나만으로는 아무것도 하지 못한다.
- 객체 사이의 협력을 하는 매게체는 메세지를 전송하는 것이다.
CRC Card
- 객체지향 설계를 할 때 가장 많이 사용하는 객체지향 설계 도구
- 실제로 만질 수 있는 물리적인 물체를 사용하여 객체들의 상호작용을 설계(실제로는 종이, 인덱스 카드)
- 객체를 나타내는 종이들을 실제로 적고, 옮기고, 배치하면서 적절한 객체간의 구조를 찾아가는 과정
- 구간별 설명: 후보, 책임, 협력자
- 후보(Candidate): 역할 또는 객체
- 책임(Responsibility): 객체의 책임
- 협력자(Collaborator): 이 객체가 이 책임을 할 때 누구에게 메세지를 보내야 하거나, 누가 필요한 지
- 객체지향은 데이터에 대한 이야기를 하지 않는다. 대신 어떤 역할이 필요한지 어떤 객체가 필요한지 이야기한다.
- 객체는 상태와 행동을 같이 가진다.
- 상태와 행동을 같이 가진다는 것은 어떤 상태가 있을 때 그 상태를 변경하고 조작하는 행동이 같이 있어야 한다.
- 그 상태를 가장 밀접하게 사용하는 행동을 그 객체에 배치해야 한다.
- 행동과 상태를 같이 넣어라. 아니면 캡슐화가 깨진다.
예매 생성 책임 할당
- 이 책임을 수행하는 데 필요한 데이터를 가장 많이 가지고 있는 객체에 행동을 할당
- 객체를 만드는 순서는 어플리케이션의 요구사항을 분석하고 그 다음에 적절한 객체를 선정
- 예매 생성은 상영에게 책임을 할당(가장 많은 데이터를 가지고 있기 때문)
- 이게 되지 않으면 별도의 팩토리와 같은 객체를 만들어야 함
가격 계산 책임 할당
- 영화 가격을 알고 있는 영화 객체에 할당
할인율 계산 책임 할당
- Discount Strategy 객체를 생성하여 할인율 계산 책임을 할당
- Movie는 Discount Strategy와 협력
할인 여부를 판단할 책임 할당
- Rule에 할인 여부를 판단할 책임 할당
- Discount Strategy는 Rule과 협력
- 개념적으로 어떤 객체들이 어떤 책임을 가져야 하는지는 어플리케이션 요구사항부터 의도
- 어플리케이션 요구사항을 바탕으로 객체들을 생성하고 역할을 부여하며 책임을 할당하고 협력하도록 함
- 이런식으로 전체 객체간의 협력관계를 통해 어플리케이션을 구현하는 것이 객체지향적으로 설계하는 것
객체지향 구현
상영(Showing), 예약(Reservation), 영화(Movie)
public class Showing {
public Reservation reserve(Customer customer, int audienceCount) {
return new Reservation(customer, this, audienceCount);
}
public Money calculateFee() {
return movie.calculateFee(this);
}
}
public class Reservation {
Reservation(Customer customer, Showing showing, int audienceCount) {
this.customer = customer;
this.showing = showing;
this.fee = showing.calculateFee().times(audienceCount);
this.audienceCount = audienceCount;
}
}
public class Movie {
public Money calculateFee(Showing showing) {
return fee.minus(discountStrategy.calculateDiscountFee(showing));
}
}
할인 전략(DsicountStrategy)
public abstract class DiscountStrategy {
public Money calculateDiscountFee (Showing showing) {
for(Rule each : rules) {
if(each.isSatisfiedBy(showing)) {
return getDiscountFee(showing);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountFee(Showing showing);
}
public class AmountDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return discountAmount;
}
}
public class NonDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return Money.ZERO;
}
}
public class PercentDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return showing.getFixedFee().times(percent);
}
}
할인 규칙(Rule)
public interface Rule {
boolean isSatisfiedBy(Showing showing);
}
public class SequenceRule implements Rule {
public boolean isSatisfiedBy(Showing showing) {
return showing.isSequence(sequence);
}
}
public class TimeOfDayRule implements Rule {
public boolean isSatisfiedBy(Showing showing) {
return showing.isPlayingOn(dayOfWeek) &&
Interval.closed(startTime, endTime)
.includes(showing.getPlayingInterval());
}
}
- Showing에 reserve 메서드
- Reservation(customer, this, audienceCount) 반환
- Reservation의 fee → Showing에게 금액 계산 할당
- showing.calculationFee().times(audienceCount)
- Showing에서 금액을 계산하는 것은 Movie에게 위임(가격을 가장 많이 알고 있는 객체가 Movie)
- movie.calculateFee(this)
- Movie는 다시 DiscountStrategy에게 얼마를 할인해야 되는지 물어봄
- discountStrategy.calculateDiscountFee(showing)
- DiscountStrategy는 n개의 Rule 안에서 루프를 돌며 하나라도 만족을 하는지 판단
- isSatisfiedBy(showing)
- 맞으면 금액을 계산하기 위해 getDiscountFee라고 하는 protected 메서드 호출
- getDiscountFee(showing)
- 각각의 서브 클래스들이 그 조건에 맞게 계산
Domain Concepts & Implementation
- 객체지향으로 설계한다는 것은?
- 객체들의 구조는 도메인 안에 들어 있는 객체들의 관계와 최대한 유사하게 클래스의 구조를 가져가는 것.
- 적절한 객체에 책임을 할당하고 객체 간의 협력 관계로 푸는 것이 객체 지향이다.
위임식(delegated), 분산식(dispersed) 제어 스타일
- 트랜잭션 스크립트와는 달리 책임이 분배되어 있는 시퀀스 다이어그램
[Part - 5] 도메인 레이어와 아키텍처
- 도메인 레이어를 어떻게 짜느냐에 따라 전체 아키텍처의 구성이 달라진다.
- 도메인 모델을 사용하면 이 안에 들어있는 것은 순수한 도메인 로직
- 도메인 로직만을 가지고 어플리케이션을 만들 수 없다.
- 이 객체를 만들기 위해 필요한 데이터를 데이터베이스에서 가져와야 한다.
- 할인 정보는 실제로 데이터베이스에 있다.
- 데이터베이스의 정보를 객체에 밀어 넣는 작업이 필요하다.
- 도메인 로직을 처리할 때 필요한 전후 작업이 있다.
- 순수한 도메인 로직이 아닌 로직을 처리하기 위한 전후 관계, 어플리케이션 flow를 관리하는 것을 어플리케이션 로직이라 한다.
[예시] DB에서 읽어오는 작업, DB 넣는 작업, 메일이나 카톡으로 push하는 등과 같은 후 작업)
- 순수한 도메인 로직이 아닌 로직을 처리하기 위한 전후 관계, 어플리케이션 flow를 관리하는 것을 어플리케이션 로직이라 한다.
- 어플리케이션 로직은 일반적으로 순수한 객체(도메인 객체) 안에 넣지 않는다.
- 의존성(dependency)이 생김(DB나 Network)
- 테스트 어려움, 응집도 떨어짐
- 도메인 레이어를 캡슐화 해야한다
- 기본적으로 도메인과 무관한 로직이 도메인에 침투하지 못하도록 막아야 한다.
- 이러한 어플리케이션 로직을 모아둔 곳이 서비스 레이어다.
- 도메인 모델 패턴으로 도메인 모델을 구성하면 그 위쪽에 서비스 레이어가 생성된다.
- 서비스 레이어의 역할
- 어플리케이션 경계
- 어플리케이션 로직 처리(application logic)
- 도메인 로직의 재사용성 촉진
- 위와 같이 분리하면 하나의 도메인 레이어를 다양한 서비스 레이어에서 재사용이 가능하다.
트랜잭션 경계
- 트랜잭션도 어플리케이션 로직이라고 볼 수 있다.
- 서비스 레이어를 만들면서 트랜잭션 경계가 생긴다.
- 서비스 레이어에서 트랜잭션 경계를 설정한다.
// 도메인 모델에서 서비스 레이어 로직
@Transactional
public Reservation reserveShowing(int reserverId, int showingId, int audienceCount) {
Customer reserver = customerRepository.find(reserverId);
Showing showing = showingRepository.find(showingId);
Reservation reservation = showing.reserve(reserver, audienceCount);
reservationRepository.save(reservation);
return reservation;
}
- 서비스 레이어는 도메인 레이어를 처리하기 위한 준비 작업(전처리), 도메인 레이어가 잘 처리되었거나 예외가 발생했을 때의 예외 처리(후처리)가 있다.
- 관심사의 분리(어플리케이션의 로직은 서비스, 도메인 로직은 도메인 레이어)
트랜잭션 스크립트를 사용할 때
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) {
fee = calculateFee(movie);
}
Reservation result = makeReservation(customerId, showingId, audienceCount, fee);
reservationDAO.insert(result);
return result;
}
- 트랜잭션 스크립트는 트랜잭션 관리, 어플리케이션 로직, 도메인 로직이 한 곳에 있다.
→ 어떤 코드를 어디서 변경해야 하는지를 찾아가기가 힘들다(숨어 있음) - 별도의 서비스 레이어가 필요하지 않다.
- 본질적으로 위 코드는 도메인 레이어가 맞다.
데이터 소스 레이어(RDB)
- 데이터 소스는 대부분 관계형 데이터베이스(RDB)를 사용한다.
- 도메인 모델을 사용할 때 테이블 설계는 어렵다.
객체-관계 임피던스 불일치
- 객체 모델과 DB 스키마 사이의 불일치를 말한다.
- 객체 패러다임과 관계 패러다임의 불일치
- 객체 패러다임: 객체들의 행위 중심, 다형성, 객체의 유연성, 재사용성(수정 용이성) 중점
- 관계 패러다임: 데이터 관점은 중복 제거 중점
- 객체 지향적으로 구현할수록 관계 패러다임과 멀어진다.
데이터 매퍼(Data Mapper)
- Data Mapper를 둬서 양쪽의 불일치를 해결해준다.
- 객체와 DB 관계를 끊는다.
- Data Mapper는 직접 구현하기 어렵다. 따라서 하이버네이트와 같은 ORM을 사용한다.
- 대표적으로 JPA가 존재한다.(Java Persistence API)
- 객체 지향적으로 개발하기 위해 DB와 객체지향이 서로의 길을 가기 위해 중간에서 연결해주는 것이 JPA이다.
- JPA를 사용한다고 해도 여러가지 제약조건이 존재하기에 순수하게 객체 지향적으로 구현하기는 불가능하다.
테이블 데이터 게이트웨이(Table Data Gateway)
- 데이터 관점 테이블과 1:1로 매핑되는 데이터 덩어리를 만들고 getter & setter를 넣는다.
- 데이터마다 DAO를 생성한다.
- DAO라는 객체가 하나의 테이블을 처리하게 만든다.(Join은 예외)
- 하나의 테이블마다 하나의 DAO라는 객체를 넣고,. 그 객체가 그 테이블을 감당하게 한다.
- 이것을 테이블 데이터 게이트웨이라고 한다.
Domain Model / Transaction Script
도메인 모델(Domain Dodel)
- 도메인 모델로 가면 서비스 레이어가 일반적으로 들어가야 한다.
- 들어가지 않을 시 도메인 모델의 독립성이 저하된다.
- Data Source는 일반적으로 ORM과 같은 Data Mapper를 써서 구성
트랜잭션 스크립트(Transaction Script)
- 일반적으로 Transaction Script를 구성할 때는 서비스 레이어가 없음.
- Data Source는 Table Data Gateway를 사용(각 테이블마다 개별 DAO를 생성)
- 전체 레이어를 관통하는 getter & setter를 가진 Anemic Domain Model을 사이에 둔다.
[Part - 6] 선택의 기로에서
무엇을 쓸 것인지 결정해야 한다.
변하지 않는 것은 모든 것이 변한다는 사실 뿐이다.
헤라클레이토스
할인을 중복으로 해야하는 요구사항이 들어왔을 경우
- 요구사항으로 금액과 퍼센트 할인 둘다 들어옴
- 여러 개의 할인을 적용할 수 있도록 로직을 수정
트랜잭션 스크립트
private Money calculateFee(Movie movie) {
List<Discount> discounts = DiscountDAO.selectDiscounts(movie.getId());
Money discountFee = Money.ZERO;
for (Discount discount : discounts) {
if (discount != null) {
if (discount.isAmountType()) {
discountFee = Money.wons(discount.getFee());
} else if (discount.isPercentType()) {
discountFee = movie.getFee().times(discount.getPercent());
}
}
}
return movie.getFee().minus(discountFee);
}
- 기존의 코드를 수정한다.
- Discount가 n개가 가능
- 1개가 아니라 n개를 select하도록 가져와서 loop를 돌려 type을 체크 후 금액을 누적시킨다.
- 기존 코드를 수정한다는 두려움
- 테스트 코드가 없다면 더욱 심화된다. 실무에서도 테스트 코드 없이 리팩토링하는 경우 존재
- 개념이 암묵적으로 숨겨진다.
- 중복 할인 로직을 코드에서 찾을 수 없다.
- 전체 로직을 모두 읽어봐야 한다.
- What이 아닌 How를 판단해야 한다.
컴포지트 디자인 패턴
public class OverlappedDiscountStrategy extends DiscountStrategy {
// ...
}
- 도메인 모델은 기존의 코드를 수정하지 않고, 컴포지트 디자인 패턴으로 해결할 수 있다.
- 기존 코드의 수정에 닫혀있는 상태에서, 새로운 코드의 추가에는 열려 있다 → OCP
- 클래스로 나눌 때의 장점: 암묵적이었던 개념을 명시적으로 표현할 수 있다.
- OverlappedDiscountStrategy라는 이름의 클래스를 추가했으므로, 이름만 보고 알 수 있다.
의존성 역전 원칙(DIP)
- 상위 레벨이 하위 레벨의 의존성을 가지지 않는다.
- 의존성 방향이 추상화 방향(High Level)으로 간다.
- 이것을 의존성 역전 원칙(DIP)라고 한다.
협력 관계와 추상화
- 추상화를 사용하면 도메인의 개념을 확 줄여서 이야기할 수 있다. 필요 시 detail로 내려갈 수 있다.
- 왼쪽 그림과 같은 협력관계로 코드를 짜면 변경이 어려움
- Domain Abstraction을 뽑고 뽑고 뽑으면 Domain Layer Architecture가 된다.
도메인 모델은 복잡성을 알고리즘에서 분리하고 객체 간의 관계로 만들 수 있다.
유효성 검사, 계산, 파생 등이 포함된 복잡하고 끊임없이 변하는 비즈니스 규칙을 구현해야 한다면 객체 모델을 사용해 비즈니스 규칙을 처리하는 것이 현명하다.
Martin Fowler
거짓말
- 영화 예매 시스템을 통해 도메인 모델의 장점을 쉽게 볼 수 있었다.
- 대부분의 현재 코드는 요구 사항 변경에 부적합
- 대부분의 어플리케이션의 요구사항은 어떻게 변화될지 아무도 모른다.
- 요구사항이 어떻게 변경될지 모르므로 코드를 최대한 간단하게 구현해야 한다.
- 변경이 필요할 때마다 리팩토링이 필요하다.
- 하지만 변경의 방향을 알 수 있다면 객체지향 패러다임이 변화를 수용할 수 있는 설계를 지원한다. 단, 변경의 방향을 알기 위해서는 경험을 쌓아야 한다.
반응형
'Review > Seminar' 카테고리의 다른 글
[우아한테크세미나]TDD와 리팩토링 (0) | 2023.09.28 |
---|