프로그래밍 왕초식(나누기)
초보 개발자의 안좋은 특징
- 점점 커지는 함수/메서드/클/래스 → 프로그램 분석이 어려워진다. 즉 비용이 증가한다.
- 예)
- if-else, if중첩이 복잡해짐
- 변수의 의미가 중간에 바뀜
- 코드 안에서 중복이 발생함
- 예)
해결법 : 나누기
- 의미가 있는 단위로 코드/구성 요소를 나누는 기술
- 나누기의 전형적인 예) 패턴
- 웹 개발 : controller - service - DAO(Data Access Object)
- DDD(Domain-Driven Design) : Entity, value, repository
- Design Pattern : builder, adapter, composite
- 나누기의 전형적인 예) 패턴
나누기는 곧 기능 분해/분리
기능은 여러 작은 기능/로직(하위 기능)으로 구성
예) 구매취소(기능) 분리
- 구매 취소
- DB에서 주문데이터 읽기
- 취소 가능 여부 확인
- 주문 데이터 변경
- 결제 취소
- DB에 변경 데이터 쓰기
- 취소 결과 통지
- 구매 취소
나누기의 결과 = 메서드나 클래스로 분리
- 나눈 하위 기능을 메서드나 클래스로 분리
나누기 전, 후
before
- 분리 전 : DB에서 주문정보를 읽어오고 order.getStatus값이 1007보다 크면 Exception 발생 아니면 1010으로 값을 변경, update 쿼리 실행하고 restTemplate으로 호출하고 insert 쿼리 실행
public void cancel(String orderId){
Order order = jdbcTemplate.query(
"select ...생략",
...생략);
if(order == null) throw new NoOrderException();
if(order.getStatus() > 1007){
throw new IllegalStatusException();
}
order.setStatus(1010);
...
jdbcTemplate.update("update ..", ...생략);
...
restTemplate.post(... 생략); // PG사 통신
...
jdbcTemplate.insert("insert ...", ...생략);
}
After
- 분리 후
- DB와 관련된 조작은 Repository로 분류 한다.
- 주문의 최소 관련된 데이터 변경 자체는 order에게
- 결제 취소와 관련된 기능은 PayCancelService로 분리
public void cancel(String orderId){
Order order = orderRepository.findById(orderId);
if( order == null) throw new NoOrderException();
order.cancel();
orderRepository.save(order);
PayCancelRequest pcr = cratePayCancelRequest(order);
payCancleService.cancelPayment(pcr);
notifier.notifyOrderCancle(order);
}
private PayCancelRequest cratePayCancelRequest(Order order){
...
}
나누기는 곧 역할/책임 도출
- 함수/클래스 분리 → 역할/책임에 따라 구성 요소 도출
나누기의 기준
- 답은 없다고 한다.
하지만
- 기능에서 “의미” 있는 하위 기능 단위로 나누는 시도
- 주문 취소 기능
- 취소 사실 통지 vs PUSH 테이블에 INSERT
- 결제 승인 취소 vs PG사가 제공하는 API실행
- 주문 취소 기능
하위 기능을 도출할 때
방법 1. 대칭성 활용
public void cancel(String orderId){
Order order = orderRepository.findById(orderId);
if(order == null) throw new NoOrderException();
order.cancel();
orderRepository.save(order);
PayCancelRequest pcr = createPayCancelRequest(order);
payCancelService.cancelPayment(pcr);
...
jdbcTemplate.upadate("insert ..", ..);
}
아래의 코드는 다른 코드들과 구현수준이 안맞는다.
주문취소 관련된 기능 입장에서 하위 기능 대응하는데
insert쿼리를 실행한다. → 이 코드는 다른 코드들과 대칭성이 안맞는다.
...
jdbcTemplate.upadate("insert ..", ..);
이와같이 코드간의 수준이 안맞을 때 맞춰서 하위 기능을 도출하는 시도를하는 습관을 갖자 !
나누기 결과
- 나누기를 안하면
- 코드 순서대로 구현을 이해하는 것이 가능
- 코드가 커질수록 코드가 복잡해져서 점점 이해가 어려워짐
- 코드가 커질수록 변경도 어려워짐
- 나누기를 잘하면
- 구조의 복잡도가 증가하지만 상위 수준에서 실행 흐름 이해하기 좋다.
- 코드 변경이 나누기 전 보다 쉬워질 가능성이 높아진다.
- 너무 잘게 나누면
- 너무 복잡해져서 실행 흐름을 이해하기 어려워진다.
- 변경할 때 수정 대상이 많아져 변경이 어려워진다.
나누면 구조의 복잡도가 증가한다. 하지만 상위수준에서 전반적인 코드 실행을 이해하는데 더 좋아질 수 있다.
또한 코드 변경이 나누기 전보다 쉬워질 가능성이 높아진다.
나누기를 잘 나눴을 때 변경의 위치가 한 곳으로 집중될 가능성이 높아질 수 있다.
초짜라면 나누기 연습할 것
- 평소에 나누기 연습을 해야 실전에서 저절로 나온다.
- 연차가 많이 쌓인 개발자도 기본적인 나누기를 못하는 경우가 많음.
느낀점
또 각성해야겠다고 생각한다. 자바지기(포비)님의 유튜브 영상을 보면 항상 의식적인 코드 짜기를 강조한다. OKKY채널에서도 여러 고수분들이 강조한다. 당장 나누기, 의식적인 코드짜기를 실현하자!! 현재의 시간을 감사히 여기자.
모든지 안되면 꾸준히 하면된다.
안되면 되게하라!
마지막으로 말로 글을 끝마치겠다.
나중은 결코 오지 않는다. -르블랑의 법칙
참고 :YouTube 채널 -최범균 https://www.youtube.com/watch?v=NaeXpswLvxk