1주차 마지막 리뷰
- 행동했는지 여부도 실패, 성공 테스트때 빼먹지말자
1 | 단위 테스트 : 대상 객체만 "실체" 로 두고 테스트 |
2주차 발제 내용 및 주의사항
https://www.notion.so/teamsparta/Chapter-2-1-1c52dc3ef51480bbb279dec0f57989c9?pvs=4
1주차에서는 통합테스트시 , 충전 동시 성공, 사용 동시 성공, 이외에도, 충전 사용 동시 모두 성공도 추가하면좋았음
단위테스트에서 또 아쉬웠던점. 서비스 포인트 충전 고유 기능에 대해 아래 부분에서 verify()를 했으면 얼마나 좋았을까
항상 고유의 기능이라는 것을 무엇일까? 에 대한 고민 -> 테스트로 진행
```
단위 테스트 : 대상 객체만 “실체” 로 두고 테스트- 대상 객체의 고유 기능
통합 테스트 : 두개 이상의 객체를 “실체” 로 두고 테스트
- 유기적인 동작의 기능
서비스.포인트 충전의 고유 기능?
- 포인트를 조회해 와서, 충전하라고 시키고, 그 결과를 저장하고 내역도 저장한다.
포인트 충전() {
포인트 조회 포인트 충전 ---> 예외가 발생해서 터졌다. 포인트 저장 포인트 내역 저장 // 얜 테스트 작성한 사람이 거의 없었다.. return 포인트;
}
verify(포인트 내역 저장이 1번 호출되었는가);
단위테스트로 검증한다면, 포인트 조회, 저장, 포인트 내역 저장 다 stubing (남에꺼니까)
–> 그리고 내가 정상적으로 동작하게끔 의도한 stub 을 뒀을 때
—> 정상 수행하고 값을 반환하는지가 서비스의 “고유 기능”
–>
verify(조회, 1번)
verify(충전, 1번)
verify(저장, 1번)
verify(내역저장, 1번)
테스트 “포인트 충전이 실패하면, 포인트 내역과 포인트 변경분은 저장되지 않는다”() {
// arrange
충전 실패되는 stub 을 세팅
// act
서비스.포인트 충전()// assert
verify(포인트 저장, 0번)
verify(내역 저장, 0번)
}1
2
3
4
5
6
7
8
9
10
11
12
13
- TDD 가 왜 중요하냐?
- 클래스 mock상관없이,
- postman으로 매번 안찔러도됨
## 반드시 해당 순서 지키기내가 기능을 개발하는 방법 ( Feat. Unit Test )
- 요구사항을 분석 ( 어떤 기능을 제공해야하는지 )
- 실패하면 안되는 Input 을 정리 ( 실패 TC 에 대해 정의 )
- 2번의 TC 를 모두 만족시킬 수 있도록 기능을 구현
- 기능의 책임을 분석
- 너무 다양한 TC 가 필요하지 않았는지
- 너무 많은 책임이 주어져 있지는 않은지 ( 응집도 )
- 리팩토링
- 적절한 위상의 책임을 가지고 있도록
- 충분히 원자적으로 테스트가 가능하도록
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
32
## 문제 정의 및 요구사항 분석
### 문제정의
- 추상적 선착순 기능 대기열 기능
- 구체적 접근 -> 대기열 기능을 제공하기위해 API , DB 어떻게 해야할까? -> 추상적인 문제점 -> 구체적인 접근으로
### 요구사항 분석
- 기능적 요구사항
- 어떤 기능을 제공해야하는가?
- 비기능적 요구사항
- 성능, 확장, 보안 등등
### 다이어그램 도구
- Mermaid
- Draw.io
- excalidraw.com
## 기본 개발 방식
- API Spec 정의 ( 문서, 나열, 정리 )
- Mock API 작성
- Swagger-UI 작성
—> 선배포 <—
위쪽 : HTTP API 통신 Base 의 요구사항에 대한 “추상적인 설계”
아래쪽 : 웹 백엔드 애플리케이션의 요구사항을 만족시키기 위한 “구체적인 설계”
- 본격적으로 “구체적”인 설계를 들어간다. ( 개발을 위한 )
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
# 2주차 공개 Q&A
- 코드를 해석할수있는 능력 + 대화능력 + 정리 능력이 중요해지는중
- MockAPI 를 제공할때, 에러 케이스(에러코드)에 대한 제공 어떻게?
- mockapi 만들때 -> swagger example 기능 사용하기 -> 에ㅓㄹ케이스에 대한 응답 스펙
- **request 검증(400 Error 까지는 mock api 작성하는 그 기간에 함께 적용해둔다)** -> request 검증까지는 만들자
- 객체 지향 설계?
- 책임이 제일 중요. 책임의 바운더리?
- 단순 동작(검증, 분기, 반복 등등)
- 비즈니스 (다양한 단순 동작 요소 + 정책)
- ```
"스토밍"을 통해서 어떤 "개념"들이 존재하는가?
그다음에 그 "개념"들에 "책임"을 부여하고,
"개념"들 간의 유기적인 소통에서 누가 "주체"인지를 보는 연습필요
더해져야 하는 것들은
추상화, 상속, 디자인패턴과 같은 "기술적", "이론적" 테크닉들이 가미되면 좋다.
시퀀스 다이어그램, erd그리는 것 -> 요구사항 분석
- “문제 정의”를 하고, 그 문제를 해결하기 위한 내 방식을 “설명”하는 수단이다.
- 요구사항의 분석 및 설계 단계
- 시퀀스 다이어그램 /ERD
- 재료 나열 (분석)
- 재료를 다이어그램의 특성에 맞게 재배열 (설계)
- 원하는 만큼 표현 ->”문제정의” -> 어떻게 -> 구체화-> 시퀀스 다이어그램, ERD, 클래스다이어그램
- 만만한 방식
- API 별로 기능별로 깔아놓는다
- 깔아놓은 거 밑에 블렛포인트로 어떤 정책이 필요할지, 어떤 동작을 해야할지 풀어적는다
- 풀어놓은 걸 재료로 각각 적합한 다이어그램을 그려나가며 구체화한다.
시퀀스 다이어그램을 그릴때, 실제 구현체 같은게 드러나지 않을때 추상적으로 큰 틀에서 나타내고, 클래스 다이어그램에서 구체적으로 실제 구현체들이 나오면되는지?
- 시퀀스 다이어그램은 큰틀 추상체만 하기. 도메인 간의 추상적인 뷰를 보기위함
- API 콜 -> 유저(도메인) -> 콘서트 -> 예약 -> 결제
- 구체적인 각 도메인에서 객체간의 관계 = 나타내고 싶으면, “클래스다이어그램”
- 클래스 다이어그램은 객체간의 책임, 경계선, 상관관계를 나타내기위함.
- Controller -> Service -> Repository는 너무 흔함. 필요없는 설명
- Service -> UserValidator, UserCreator 같은것들은 표시 필요
- User, Product, Order 애내들은 상관관계
- 시퀀스 다이어그램은 큰틀 추상체만 하기. 도메인 간의 추상적인 뷰를 보기위함
인기 상품에 대한 기준이 과제 내용에서 최근 3일간 최대 판매량 5개로 나와있는데, 기준잘정하기
- 기준을 잘 정해라, 언제부터 언제까지 집계?
5주차까지의 마일스톤
- 설계, 단위기능, 통합기능, 서버 구축
- 마일스톤 과제
- 개발해야하는 기능별 TASK
- 설계하는 TASK는 지금 세분화 필요
- 이번 주차 꺼는 TASK깔아두고 우선순위 정도 지정가능
도커 설정
- 필요없음
이번주차 과제 API 작성까지만 인지 service 기능까지 구현?
- 과제의 mockAPI 내가 생각한 대로 응답하는 자동 응답기를 만들기
- 이번주는 요구사항 분석
- 설계(각종 다이어그램, 문서화)
- 가능하다면, mockAPI에 대해 내가 생각한대로 API spec이 동작하는지 E2E TEst정도
상품 <1:1> 상품재고로 설계할 경우, 상품 Service, 상품 재고 service로 분리? 또는 상품 Service만 생성후 상품 Repository 와 상품 재고 Repository 2개의 의존성 갖는 방식 어떤게좋은가요?
저는 상품이라는 도메인 경계로 나눠서 활용합니다.
- 하위 예시는 마치 테이블 기반의 개발방식과 동일하다. 왜 상품과 상품재고를 분리해야하지?
ProductService // 상품관련된 무언가를 하는 주인{ ProductRepository // 상품관련된 무언가를 가져다주는 하인 = interface ProductstockRepository // 어..? 얘는 상품 떼어내면 가치가 있나? 그럼 ProductRepository 에 편입시키는 게 맞지 않을까? }
interface ProductRepository { //이렇게해야 도메인 주도적이지않을까? fun getProduct (productId: Long): Product? fun getStock(productId: Long): ProductStock? }1
2
-user ㄴ userController ㄴ application-service ㄴ domain/service/- ㄴ domain/user1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
- 상품 재고 옵션 기능 요구사항
- 당연히.알아서 보강
- confluent WIKI, notion, github readme.md, docs/xx.md, GitHub wiki 등 사용하여 UML
- 시퀀스 다이어 그램은 API별로 하나씩?
- 최소 단위로 API별, 내부시스템 별로도 가능
- 예를 등면, 주기적으로 어떤 기능이 실행(스케줄링) 도 시퀀스 다이어그램잇어야한다
- 거기에 더해, 유저의 행동기반, MSA에서 통신에 대해서도 해볼수있음
- Mock API 에서 특정 에러 값은 특정 인풋으로 요청했을때, 에러반환
- swagger Example활용해서 description과 같은 설명 란에 미리 작성
- 추후에 구현되었을때(검증 등등) -> 그때 해당 문서를 교체해주는방식 좋음
- 다수의 인스턴스 환경에서도 문제없이 할려면 어플리케이션 설계?
- 요거는 5주차
- 같은 API서버가 여러대ㅔ 떠잇고, 앞에 로드밸런서가 있고, 로드밸런서가 서버 골라서 요청줌
- 과제 신경 써야할부분:
- 마일스톤 = 작업을 쪼개는 것은 자세히 하는데, 어떻게 배치할지나 순서를 빡빡하게 가져가진 않는다.
- 다이어그램=그 그림들을 처음부터 끝까지 보았을 때, "내가 뭘 구현하고자 하는구나" 를 알 수 있는 정도는 되어야 한다.
- 시퀀스다이어그램= API 별로 내부시스템(스케줄러, 배치 등)는 최소한 있어야 하고..
- 클래스다이어그램= 도메인 객체간의 관계 정도는 있어야 하고..
- ERD = 테이블들과, 테이블들의 속성, 그 테이블들간의 관계까지는 있어야 하고..
- 상태다이어그램 어떤 상태들이 존재하고, 그 상태간의 전이"는 어떤 동작에 의해 수행되는지는 있어야 하고.. 나올 수 있는 상태들은 명확해야 한다.
- 상태 다이어그램 예시(주문의 상태)(상태를 구체적으로 하는데에 집중)
- 주문서 생성됨
- 주문 요청됨
- 결제 요청됨
- 결제 완료됨
- 배송중
- 배송 완료됨
- 주문 취소됨
- 환불 신청됨
- 환불 완료됨
- 비기능적 요구사항에는 어떤 것들?
- 개발자들이 하는 대부분의 행위
- 조회 성능 1s->0.5s로 줄인다
- 코드 가독성, 유지보수, 확장
- **테스트 코드는 기능적 요구사항을 검증하는 도구** 이자, 보안, 유지보수성, 이식성 등의 "비기능 요구사항"을 충족하는지 검증하는도구
- 소규모 프로젝트라고 생각하고 ERD를 작성중인데, 사용자 TABLE에 충전금액이 맞을까요? 분리하기엔 큰 규모가 아니라서 또 애매하다고 생각이들어 질문드립니다!
- 여러분들이 생각하기에 비기능적"으로 분리하는 요소가 필요할까? 라는 고민..
- 큰 규모가 아니여도, 분리해야 할 수도 있고 큰 규모여도 분리하지 않아도 될 수도 있다.여러분들이 동작이나 관련 객체를 어떻게 디자인하느냐에 따라서 달라져요!TDD 때 말했던 거 기억나시는지..?
- 개발자란 . . .?
- "일단 되게 해라"= 기능적 요구사항을 만족시켜라.
- 허락이 된다면.."= 내 비기능적 욕심을 충족시켜라.
- 그래서 저라면.. 기능적 요구사항을 만족시킬 수 있는 최소스펙으로 다 작성한 후에, 고민할 것 같다.
# 2주차 멘토링
- c4다이어그램 이용해보기
- https://c4model.com/diagrams/container
- 논리적 설계 후 flowchart그리기.
- 추후에 물리적(mysql, redis 등등) 선택하는거임.
- Sequence Diagram -> 및에 dto 데이터 변경되는것도 보여주면좋음
- flow chart
- ERD
- domain class diagram 그려주기
- 작업단위 쪼개기
- 컨트롤러 구현 5.5md
- 대기열 구현
- 테이블 설정
- 설계가 훨씬더 시간을 많이 써야함
- 설계 15일 개발 일주일..등등
Q1. 시퀀스 다이어그램에 예외처리(alt, opt)가 필요할까요?
A1. 도메인 별로 더 중요한 맥락에 대한 피드백을 유저에게 줘야함. 중요한 예외만 넣어도됨.
- 예외처리가 너무 과해지면 (Interal 에러인경우 이런것들을 구별할 필요는 없다.)
비즈니스적 맥락에서 필요한 에러들을 표기해주는게 좋다.
Q2. 코치님이 생각하시는 “좋은 설계” 란 무엇일지 궁금합니다.
A2. 코드 아키텍처의 설계인건지? 프로젝트 전체의 설계인건지?
- Readme에 어떤 내용이 들어가야할지?
- 모두가 설계문서를 읽고, 복잡한 도메인의 정책을 쉽게 이해할 수 있어야한다.
- 어려운걸 쉽게 이해하게 만들어야한다. -> 각종 도식화된 다이어그램들이 필요
- 줄글로 200줄 보다 그림 하나가 낫다
- 직군에 상관없이, 모두 동일한 개념을 동일한 언어로 의사소통 해야한다
- BestPractice
- https://github.com/samchon/shopping-backend
- https://docs-pay.toss.im/tutorial
- DB ERD 부터 시작해서, ERD를 해석해놓은 것들 등등
- ERD를 잘그리기, 여러 도메인들끼리의 상호작용을 어떻게 잘그리는가 -> A기능에서 동작시 B, C도메인이 각각 어떤 상태로 변경되어야한다
- 쿠폰이 발급됨 -> 사용됨으로 바뀌는 시점
- 상품 주문시 쿠폰을 사용했을때
- 쿠폰이 사용됨 -> 발급됨으로 바뀔수있는데, 이건 언제?
- 주문 취소, 쿠폰 사용후 트랜잭션 롤백
- 쿠폰의 상태가, 다른 도메인의 영향을 받아서 변경되는 것들
- 이걸 어떻게 잘 표현했는가 ? 쿠폰사용 정책
- 정책: 해당 도메인 자체가 가지고있는 제약조건들
- 쿠폰은 발급됨, 사용됨, 만료됨 상태를 가진다
- 쿠폰의 종류엔는 퍼센트 쿠폰, 정액권 할인 쿠폰있다
- 상태가 어떻게 변경되는가(도메인간의 상호작용)
- 이 모두 잘 기술 되어야 개발편하고 의사소통도 편하다.
Q3. 시퀀스 다이어그램 도메인을 어떻게 분리해야하나요? Service 계층 대신에 아래 그림처럼 Client와 각각의 도메인들(user(사용자), userPoint(금액))로 분리를 해도 괜찮나요?
A3.
- C-S-R 등의 기술적 레이어에 따른 시퀀스 다이어그램은 각 도메인의 기술적인 것들을 도식화하고싶을때 쓰면된다.
- 현재는 도메인 간의 관계를 나타내는 것에 집중
- Client -> 충전 요청 -> User, UserPoint
- (충전 요청)과 같은 기능단위의 레이어 표기 추천 -> 에러를 표기하기 더 쉬움
Q4. API 설계 시 실무에서는 RESTful API의 원칙에 맞게 설계하는지 궁금합니다
A4. 원칙 잘 못지켜질때도잇다
Q5. 주문 결제를 할 때 주문 따로, 결제 따로 API를 구성하려고 했습니다.
- 각각의 API에서 사용자가 맞는지 검증을 한다면 User 도메인이 필요한데, 그럼 시퀀스 다이어그램을 작성할 때 User부터 시작해서 주문, 상품 등등 까지 다 시퀀스 다이어그램으로 나타내야 할까요?
(사실 주문 / 결제에서 User 검증이 꼭 필요한가도 의문입니다.)
- 주문을 할 때 재고 차감을 생각하고 있는데, 주문서만 만들고 결제를 안하는 상황이 발생하면 좀 이상해지는 것 같아요.
주문 말고 결제할 때 제고차감을 하는 게 맞을까요?
A5.
1. 재고차감을 주문 API에서 해도 무방하다.
2. 결제 생성 / 완료 상태가 실제로도 분리되어 있음으로, 이상한 플로우는 아니다.
3. 주문에서 유저가 없는경우 이런게 사실상 말이 안되는 케이스고, 비즈니스 맥락에 크게 중요하지 않으니 생략해도 된다.
- 주문에서 상점정보는 중요하게 작용할 수 있다.
1. 상점의 결제가 막혀있는지 아닌지 는 중요할 수 있다, 그럼 포함되어야함
2. 00시에 문을 닫는 가게인데, 59:59 초에 주문 -> 결제할때는 가능했겠지만 승인시점에는 아닐 수 있다. (닫힌가게입니다 등을 보겠죠)
Q6. 주문할 때 Product, Order, OrderProduct 로 나눌 생각입니다. (한 번 주문할 때 여러 상품을 선택할 수 있고 상품도 여러 주문에 담길 수 있어서)
시퀀스다이어그램에서 다대다를 풀기 위한 OrderProduct를 작성할 필요는 없고, 그냥 Product와 Order의 도메인간의 상호작용만 나타내면 될까요?
OrderProduct 관련해서는 클래스 다이어그램이나 ERD에서 보여주면 되겠죠?
A6. 다대다를 푸는건, DB때문 (참조방향의 문제떄문에) -> 때되면 기술적으로 접근
- A:B (N:M), A:(1) AB:(1) B
Q7.
- 개인적으로 패키지 구성에 대해서 질문이있습니다.
- controller, application-service, domain, domain-service, infra 로 나눌려고 하는데 조언해주실수있는지? → 해당 구조를 가져간다면, 비즈니스 로직들 테스트 하기도 편한 구조로 생각됨
- controller - 외부 요청에 대한 값 검증 및 해당 요청사항을 처리하는 application-service 를 호출 책임
- application-service - 각 도메인 혹은 domain-service(두개이상의 도메인 처리가필요한경우사용)을 호출하여, 비즈니스 로직처리
- domain - 하나의 도메인의 데이터 + 하나의 도메인의 비즈니스 로직을 가짐
- domain-service - 두개 이상의 도메인 처리가 필요한 경우 사용→ 비즈니스 로직 가짐
- infra - 외부 요청
A7.
application
ㄴ user
ㄴ presentation
ㄴ /controller/UserController.kt
ㄴ application
ㄴ /service
ㄴ domain
ㄴ user
ㄴ enum/~
ㄴ infrastructure
ㄴ payment
// …
이 구조의 장점:
- msa로 쪼갤때 편하다.
- 코드를 수정하는 사람의 관점에서는: 도메인 패키지에 가서 수정할 수 있다.
이 구조의 단점:
- public으로 열어야한다.
코드 수정의 지역성:
도메인끼리 모여있으면, 도메인 내에서 수정하기는 편한데, 특정 기능단위로 보기가 어렵다.
결제 생성 -> payment/application
특정 API를 수정하고자 하는 니즈가 있을때, 디렉토리를 너무 많이 옮겨다녀야한다.
commerce/<user, payment>/<presentation, application…> vs commerce/<presentation, application>/<user, payment>
각각의 디렉토리 구조에 대한 장단점을 생각하면서 내가 왜 이 디렉토리 구조를 설계했는지 설명이 가능해야한다.
특정 API를 위해서, domain들을 조합해야하기떄문에 생긴다.
그래서 facade 를 쓰는것 (userService, paymentService를 facade에서 호출해서 사용함.
1 |
|
히스토리 저장 방식에 따라서도 다르다.
- 히스토리에 대한 row를 1개씩 쌓는다.
결제시마다 매번 히스토리 저장하기
- 장점: 실시간성
- 단점: api의 많은 책임
- 상위상품 조회 API에서는 어떻게 쓰는가?
- 날짜를 기준으로 조회해서, 상위에 있는것을 통계를 낸다.
- 테이블 풀스캔을 할거다. 집계함수를 써서 상위 5개를 뽑는다.
- Q.날짜기준 조회 뿐만 아니라, 시간단위 조회를 지원할 줄 알았다.
스케줄러로 갱신하기
- 장점: api에서 고려하지 않아도 된다.
- 단점: 별개로 돌아가서 유지보수 포인트가 늘어난다.
- 상위상품 조회 API에서는 어떻게 쓰는가?
- 풀스캔을 한다. -> 스케줄러에서 결 제 성공한 모든 결제건을 히스토리 테이블에 일괄로 insert로 한다.
- 집계함수를 써서 상위 5개를 뽑는다.
- Q.별도 테이블을 두고, 여기에 결과를 쌓아서 이걸 그대로 보여줄 줄 알았다.
각 방식의 장단점 + 그걸 활용하는 쪽에서의 장단점을 고려하지 않을채로 설계를 한 것
매번히스토리 저장하기 외의 다른 방식은 뭐가 있을까?
스케줄러로 일간데이터를 미리저장하든 등등
1 |
|
동시에 요청한다. -> A,B두 작업중 뭐가 먼저처리될지 모른다.
현재 포인트: 500원
- A요청: 500원 충전
- B요청: 1000원 사용
A->B: 최종포인트 잔액: 0원, A: 성공 B: 성공
B->A: 최종포인트 잔액: 1000원, A: 성공 B: 실패
이 두가지를 모두 테스트하면됨
어짜피 랜덤하게 AB BA든 둘중 한개의 결과가 나올것
최종 포인트를 조회해본다. + A,B각각의 성공 실패여부에 따라서 판단한다.
최종포인트가 0원이다 -> A와 B가 모두 성공했는가.
최종포인트가 1000원이다 -> A는 성공하고 B는 의도된 에러에 의해 실패했는가.
Q11. 이슈(github issue)를 사용하신다면 어느정도의 사이즈로 이슈를 나누시나요?
불필요하게 이슈를 작게 나누게 된다면 트래킹하기에 어려운 점이 있지 않을까? 점도 궁금합니다.
A11. 깃헙 PR에는 project, milestone을 설정할 수 있다. 프로젝트/마일스톤을 상위에 두고, 이슈는 굉장히 작게 찢는게 좋다.
- 하나의 이슈가 다루는 내용이 너무 크다 -> PR 1개가 굉장히 커진다.