클라이언트는 제품의 정보가 수렴하는 곳.
프로젝트는 관리하기 어려워집니다.
이 모든 카테고리가 수렴하는 곳이 클라이언트, 즉 웹프론트엔드 입니다.
이렇게 여러가지 서비스를 제공하는 하나의 프로젝트를 어떻게 구성할 것인가에 대한
저희팀의 첫번째 답은,
Slide 8
Slide 8 text
%PNBJO
बਵ۽ࣗਝযܳࢸ҅ೞחѪ
%PNBJO
그러나 DDD는 아닌..
Slide 9
Slide 9 text
%PNBJO
बਵ۽ࣗਝযܳࢸ҅ೞחѪ
%PNBJO
ex : , , ..
그러나 DDD는 아닌..
Slide 10
Slide 10 text
-FHBDZ"SDIJUFDUVSF - ..
1SFTFOUBUJPO
%BUB
%PNBJO
&OUJUZ
Layer
Layer
use case Layer
Layer
-BZFSܻ࠙৬ઓࢿੑী'PDVT
전체 구조를 이렇게 4가지 레이어로 분리했습니다.
이 구조는 Layer의 분리와
의존성주입에 Focus를 맞추어 설계했습니다.
Slide 11
Slide 11 text
-FHBDZ"SDIJUFDUVSF - ..
얼마전 발표했던 CleanArchitecture in Banksalad의 마지막 장표인데요,
그 때 이 구조를 소개할 때 장점으로 ‘View는 Service만 알면 되고, 그 행동은
주입받은 리소스에 의존하기 때문에 이 규칙만 이해하면 빠르고 규칙적인 개발이
가능하다고 말씀드렸었습니다.
Slide 12
Slide 12 text
-FHBDZ"SDIJUFDUVSF - ..
기존 구조는 이렇게 Context에서 apiProvider, service 등 프로젝트에 필요한
요소를 모두 모아서 하나의 인스턴스로 생성하는 형태였습니다.
Slide 13
Slide 13 text
Context - 대운동장 Presentation
services
repositories
View - Presentation Layer
- router, component 등
WJFX TFSWJDF
core
Service - Domain Layer
http - Data Layer
data
Repository - Data Layer
EBUBSFQPDPSFSFQPܳ*NQMFNFOUT
DPOUFYUTFSWJDFTחDPSFTFSWJDFܳݽইم
Entity - Entity Layer
Repository - Domain Layer
Interface
-FHBDZ"SDIJUFDUVSF - ..
Slide 14
Slide 14 text
-FHBDZ"SDIJUFDUVSF - ..
이 구조를 한장에 모아보면 이렇게 정리할 수 있습니다. 보라색의 Presentation Layer는 오직 context의
service만 알게되는 형태이고 갈색의 services에서 각 서비스는 Repository들을 주입받습니다.
context의 services안에 있는 Repositories는 파란색의 data영역의 Repository들을 모아둔 것입니다.
Repository는 core에서 interface영역을 정의하고 data에 실제 구현체 부분이 있습니다
사실 이렇게만 설명드리면, 기존 구조가 어떠한 흐름이었는지 설명드리기에 충분하지 않을 것 같습니다.
Slide 15
Slide 15 text
Context - 대운동장 Presentation
services
repositories
View - Presentation Layer
- router, component 등
WJFX TFSWJDF
core
Service - Domain Layer
http - Data Layer
data
Repository - Data Layer
EBUBSFQPDPSFSFQPܳ*NQMFNFOUT
DPOUFYUTFSWJDFTחDPSFTFSWJDFܳݽইم
Entity - Entity Layer
Repository - Domain Layer
Interface
-FHBDZ"SDIJUFDUVSF - ..
Slide 16
Slide 16 text
-FHBDZ"SDIJUFDUVSF - ..
하지만, 오늘 발표에서 중점적으로 다루고자 하는 내용은 옛 아키텍처에 대한 flow가 아니므로
여기서 view부분과 repository부분만 살짝 보고 가려고 합니다.
Slide 17
Slide 17 text
-FHBDZ"SDIJUFDUVSF - ..
먼저 View 부분입니다. 예시로 포켓몬 필드라는 컴포넌트를 만들어 보았습니다.
Slide 18
Slide 18 text
new Context를 통해 생성한
Application의 service호출
-FHBDZ"SDIJUFDUVSF - ..
아까 보았던 Context를 new Context를 이용해서 Application 이라는 이름으로 생성했고,
이 구문을 통해 생성한 Application의 service를 호출합니다.
Slide 19
Slide 19 text
service중 포켓몬 수집 서비스 참조
-FHBDZ"SDIJUFDUVSF - ..
그리고 많은 서비스중 ‘수집 서비스’ 를 호출하고,
Slide 20
Slide 20 text
수집 서비스의 catch 함수 호출
-FHBDZ"SDIJUFDUVSF - ..
수집서비스가 수행하는 동작중 하나인 catch를 호출합니다.
interface로 정의된
Repository를 주입받는다.
catch৬ call ࢎਊ use case۽ ࠅ ࣻ णפ.
ਃೠ ੋ ৻ࠗ ܻࣗझח Ӓ ޖب աఋա ঋणפ.
ࠗ࠙ ,߸ೡ ࣻ ח ਃࣗ۽ࠗఠ ܻ࠙दఃӝ ਤೣੑפ.
-FHBDZ"SDIJUFDUVSF - ..
view가 부르게 되는 각 서비스는 이렇게 interface형태로 된 Repository를 주입받습니다.
이 코드를 보면 Service가 주입받는 repo는 하단에 interface형태로 된, 실제 구현체가 아닙니다.
catch와 call은 사용자의 useCase, 즉 행동으로 볼 수 있고 이 단계에서는 필요한 직접적인
외부 리소스가 명시되어 있지 않습니다.
Slide 23
Slide 23 text
-FHBDZ"SDIJUFDUVSF - ..
service에 주입해주었던 interface형태의 실제 구현체 부분입니다.
Slide 24
Slide 24 text
-FHBDZ"SDIJUFDUVSF - ..
필요한 storage & api를 주입받는다.
api와 storage는 ‘외부 리소스’로 볼수 있고 이런 외부 리소스는 이 단계에서 주입받습니다.
Slide 25
Slide 25 text
ਬ೯ز VTF$BTF
߸ೞঋҊ
: Domain Layer
이렇게 하면 유저의 행동, 즉 useCase인 catch와 call을 정의한 interface형태의 Repository는
변하지 않고,
Slide 26
Slide 26 text
৻ࠗઓࢿ݅߸҃оמ
ਬ೯ز VTF$BTF
߸ೞঋҊ
외부 의존성에 해당하는 storgae나 api 설정만 변경할 수 있습니다.
만약 storage나 api를 다른것을 사용하고 싶다면 interface영역은 수정하지 않고
data layer의 Repository생성자만 수정하면 됩니다.
Slide 27
Slide 27 text
https://speakerdeck.com/soyoung210/clean-architecture-in-banksalad
-FHBDZ"SDIJUFDUVSF - ..
사실 이 설명만으로는 기존 구조에 대해서 충분히 말씀드리지 못했고, 아마 이해가 잘 안되실것
같다고도 생각됩니다. 하지만 오늘 중점적으로 다룰 부분은 이 구조에 대해서가 아니라, 이사 과정에
대한 이야기 입니다.
그럼에도 불구하고, 혹시 이 구조에 대해서 더 알고싶은 분이 계시다면 위 발표자료를 참고하시면
좋을것 같습니다.
Slide 28
Slide 28 text
ࢎܳ೧ঠѷ
Slide 29
Slide 29 text
ࢎܳ೧ঠѷ
৵
언뜻 보면,테스트도 쉬울것 같고 괜찮은것 같은데 왜 이사를 해야겠다고 결심했을까요?
Slide 30
Slide 30 text
ࢎܳѾबೞ؍ૉʜ
그 얘기를 하기전에, 우선, 이사를 결심하던 즈음 있었던 변화에 대해 먼저 말씀드리겠습니다.
Slide 31
Slide 31 text
ਢ
Ә୶ୌ
٘୶ୌ
PFM
ݒѢ
୶ୌ
…
8FC3FQPীח
기존에 웹 레포에는 당연히 웹서비스에 대한 내용이 관리되고 있었습니다.
Slide 32
Slide 32 text
ਢ
Ә୶ୌ
٘୶ୌ
PFM
ݒѢ
୶ୌ
…
8FC3FQPীח
ࠁ
٘
ਢ࠭
…
하지만, 앱이 출시되면서 앱내 웹뷰를 위한 코드가 추가되기 시작했습니다.
처음에 이 웹뷰를 빠르고 효율적으로 개발하기 위해 하나의 레포에서 개발을 진행했습니다.
Slide 33
Slide 33 text
8FC3FQPীח
+
PC
처음 지었던 집은 PC서비스만을 고려해서 지은집이었는데,
Slide 34
Slide 34 text
8FC3FQPীח
+
PC
웹뷰 작업을 위한 코드들이 하나 둘 추가되면서
Slide 35
Slide 35 text
8FC3FQPীח
PC
PC +
웹이 살던 작은 집은 기능이 많이 붙은 다소 크고 이상한 집이 되었습니다.
Slide 36
Slide 36 text
8FC3FQPীח
..
(a.k.a useCase)
useCase
많은 서비스를 관리하기 위해 코드량도 정말 방대해졌고, 특히 사용자의 행동을 정의하는
useCase쪽 파일은 정말 크기가 어마어마해 졌습니다.
Slide 37
Slide 37 text
build .gif
큰 몬스터 집이 된 웹 집은 빌드 돌렸을때는 우선 컴퓨터가 이륙하려고 했고
가끔 메모리가 부족한 상황에서 돌리면 죽기도 했습니다.
Slide 38
Slide 38 text
: Entity ..?
그리고 이렇게 많은 서비스를 관리하다 보니 피쳐를 추가할때, 도메인 개념 단위인
Entity를 추가해야 할지 말아야할지도 헷갈렸습니다.
Slide 39
Slide 39 text
:
☠
entity
? ..?
Entity라는 폴더내에 이렇게 많은 것들이 관리되고 있었기 때문입니다.
entity의 홍수같은 느낌이죠..? 늘 새로 정의해야하나 아니면 이미 정의되었나 찾아보는 것도
매우 고통스러운 일이이었습니다..
Slide 40
Slide 40 text
: web .
develop master ?
: .. Cherry Pick
QA
Slide 41
Slide 41 text
( )
: web .
develop master ?
: .. Cherry Pick
QA
Slide 42
Slide 42 text
이런 이유로 저희팀은 각 서비스별로 저장소를 분리하기로 했습니다.
분리 하고 프로젝트를 셋업하는 과정에서 과연 기존 구조와 동일하게 유지할 것인가에 대한
고민을 했습니다. 저희팀의 결론은 ‘기존 구조를 유지하지 말자! 였습니다.
Slide 43
Slide 43 text
ѐࢶ೧ঠೡѪ
Slide 44
Slide 44 text
1. core ف ࠗ࠙ਵ۽ ա
정의부 구현부
ѐࢶ೧ঠೡѪ
첫번째는,Repository를 Interface와 실제 구현부로 나누어 정의하는 것에
효용을 느끼지 못했습니다. 사실, 이 당시에는 TestCode 작성이 고도화 되어 있지 않아서 일수도
있지만, 이 때의 상황에서는 개발을 진행함에 있어 아무런 이득을 느끼지 못한다는 결론을 내렸습니다.
Slide 45
Slide 45 text
~✌
정의부 구현부
ѐࢶ೧ঠೡѪ
1. core ف ࠗ࠙ਵ۽ ա
Slide 46
Slide 46 text
2. ٜ݅ٸח જওחؘ…
: featureܳ ઁ ײইঠ ѷ.
ѐࢶ೧ঠೡѪ
두번째로 개선해야 했던 점은, 서비스를 부수는데 너무 어려웠다는 것입니다.
Slide 47
Slide 47 text
ѐࢶ೧ঠೡѪ
2. ٜ݅ٸח જওחؘ…
,7*&8-
부수기 위해 파일을 제거하는 작업을 진행했습니다.
일단 관련된 view 파일들 부터 깔끔하게 휴지통으로 보내봤습니다.
Slide 48
Slide 48 text
core
Service - Domain Layer
Entity - Entity Layer
Repository - Domain Layer
ѐࢶ೧ঠೡѪ
2. ٜ݅ٸח જওחؘ…
view를 휴지통으로 보내고 나니, 해당 view에서 어떤 Repository와 Entity를 사용하고 있는지
파악하는 것이 어려웟습니다.
Slide 49
Slide 49 text
Repository
Dependencies
repo
기존 코드에서 관리하고 있던 ServiceDependencies 파일입니다.
RepositoryDependencies로 주입받은 내용중 각 서비스에 맞는 repo를 선택해서
service를 생성할 때 넣어주게 됩니다.
Slide 50
Slide 50 text
Repo RepositoryDependencies
,
.
삭제할 서비스가 repo.service3을 참조하고 있었다면, 이 repo.service3을 사용하고 있는 다른 서비스가
생각보다 정말 많았습니다.
Slide 51
Slide 51 text
Repo RepositoryDependencies
,
.
service ..?
repo.service3이 삭제할 서비스에만 사용되는지, 다른 서비스에서 얼마나 참조되고 있는지
파악하는것은 정말 어려운 작업이 되어버렸습니다..
Slide 52
Slide 52 text
Repo RepositoryDependencies
,
.
거대한 repository를 삭제하는 것은 너무 두려워서 결국 view쪽 코드와 entity 코드 일부만
삭제하는 것으로 결정했습니다.
Slide 53
Slide 53 text
: ৬! ӝמ ୶о غযਃ.
Entity
core/Respository
core/service
data/http/mapper
data/Repository
view
2. ٜ݅ٸח જওחؘ…
ѐࢶ೧ঠೡѪ
부수는 과정도 만만치 않지만, 수정하는 작업또한 어려웠습니다.
하나의 수정에 대해 건드려야 하는 파일들이 너무 많았기 때문이죠. 덤으로, view쪽 구조가
data와 얽혀있어서 수정하는 것은 늘 두려웠습니다.
Slide 54
Slide 54 text
3. View৬ Dataо ೠҔী
ѐࢶ೧ঠೡѪ
https://notefolio.net/virgintruth/69097
api response
api fetchState
localState
view helper
render
VIEW
마지막으로, view에 render외에 너무 많은 정보가 관리되고 있었습니다.
api response, fetchState 그리고 렌더링에 필요한 local state, 데이터를 가공하기 위한
helper function등이 제대로 분리되어 있지 않았습니다.
Slide 55
Slide 55 text
1. core ف ࠗ࠙ਵ۽ ա
2. ٜ݅ٸח જওחؘ…
3. View৬ Dataо ೠ Ҕী
ѐࢶ೧ঠೡѪ
정리하자면, 기존 구조에서는 이렇게 3가지 불편함이 있었습니다.
이는 기존에 사용했던 구조의 문제점이라기 보다는, 그 사용에 있어서 면밀히 고려하지 못했기
때문이라고 생각합니다.
ࢎೡٸח؊જਵ۽
Repo CMS
0. ࢲ࠺झ ܻ࠙
service
앞서 말씀드렸듯, 구조에 대한 변경을 결정하기 전에 저희팀은 서비스별로 저장소를 분리하자 라는
결정을 했습니다.
이런 상황이다보니 구조를 개선하는 작업은 기존 레거시를 모두 고치는 것이 아니라
비교적 작은 단위인 하나의 서비스부터 적용하는것이 가능했습니다.
Slide 58
Slide 58 text
ࢎೡٸח؊જਵ۽
1. ৬ ҳഅਸ э
core
아까 말씀드린 첫 번째 불편함은
core의 Repository영역이 정의부와 구현부로
나뉘어져있다는 것입니다.
Slide 59
Slide 59 text
ࢎೡٸח؊જਵ۽
Dependencies
repo
1. ৬ ҳഅਸ э
Slide 60
Slide 60 text
ࢎೡٸח؊જਵ۽
Dependencies
repo
1. ৬ ҳഅਸ э
이를 하나로 합쳐, api provider라는 의존성을 바로 주입받고 RepositoryDependencies라고 하는,
모든 Repository를 모아두는 쪽에 바로 주입하는 형태로 작성했습니다.
코드량이 반으로 줄어드니까, 개발 단계에서 행복감은 두배로 상승했습니다.
Slide 61
Slide 61 text
ࢎೡٸח؊જਵ۽
2. ࣻী ਊೞب۾
Entity
data/Respository
presentation/Container
store(redux)
Entity
core/Respository
core/service
data/http/mapper
data/Repository
view
( )
Before After
Slide 62
Slide 62 text
ࢎೡٸח؊જਵ۽
2. ࣻী ਊೞب۾
Entity
data/Respository
presentation/Container
store(redux)
Entity
core/Respository
core/service
data/http/mapper
data/Repository
view
Before After
두번째 개선은 첫번째 내용과 겹치는 부분도 있습니다.
수정사항이 생겼을 때 변경해야하는 파일의 양이 너무 많았었습니다.
구조를 간단하게하고, 역할을 명확히 함으로서 수정이 두렵지 않도록 했습니다.
Slide 63
Slide 63 text
ࢎೡٸח؊જਵ۽
3. View৬ Data ܻ࠙
redux + redux observable
view Data
view와 Data를 분리하기 위한 목적으로, redux와 redux middleware를 도입하였습니다.
ࢎೡٸח؊જਵ۽
3. View৬ Data ܻ࠙
store는 server에서 받아오는 데이터를 명시하는 역할로 사용했습니다.
api호출과 관련된 처리는 미들웨어에 모두 위임하였습니다. view에서는 직접 api 호출과 관련된
로직을 관리하지 않고 오직 dispatch와 Store의 state만 바라보면 되는 구조로 변경하였습니다.
Dispatch를 담당하는 곳을 한 레벨로 지정하는것까지 적용하여 각 요소의 역할을 나누었습니다.
Slide 66
Slide 66 text
بݶҡଳѐࢶ
1. ৬ ҳഅਸ э
2. ࣻী ਊೞب۾
3. View৬ Data ܻ࠙
core
( )
view Data
이 프로젝트 중반정도까지, 이정도면 기존 구조에 비해 생산성도 높고 편하다고 생각했습니다.
Slide 67
Slide 67 text
ޖਸ֬ଢ଼ਸө
Store , dispatch ?
mapStateToProps .
mapDispatchToProps Route View .
dispatch
: dispatch
!
( : entry/view , : entry/container)
( hook react-redux 6.x)
중반까지라고 말씀드린 이유는, 끝날쯤에는 놓친부분들이 많이 보였기 때문입니다.
당시에는 react-redux 6버전을 사용하고 있었는데요, 해당 버전을 쓰신 분들이라면 아실
mapStateToProps와 mapDispatchToProps 즉 store와 connect되는 부분을 어디로 잡을지에 대한
고민이 있었습니다.
그리고, 이 고민을 썩 잘 해결하지 못했습니다.
dispatch는 상태를 변화시킬 수 있으니까 가급적 한 곳에서만 이루어져야 한다고 생각했고
이 위치가 route의 바로 하위 레벨인 entry의 view폴더에서 이루어져야 한다고 생각했습니다.
Slide 68
Slide 68 text
Root
entry
/view
/user
Container Container Container Container
/search
/login
dispatch !
entry
/view
entry
/view
그림으로 보면 이런 구조인데요, Route를 모두 모아두는 rootRoute하위 레벨인
entry에서 dispatch가 이루어지도록 했습니다.
Slide 69
Slide 69 text
ޖਸ֬ଢ଼ਸө
Store , dispatch ?
( hook react-redux 6.x)
mapStateToProps .
mapDispatchToProps Route View .
dispatch
Drilling ⚓, Component
: dispatch
!
( : entry/view , : entry/container)
❓
이렇게 생각했던 것은 끊임없는 Driling과 재사용 불가능한 Component를 만들었습니다.
redux를 사용했던 목적이 Driliing해결은 아니었지만 실제 필요한 컴포넌트에게 전달되기까지
몇단계를 거치는 구조는 다소 고통스러웠습니다. 그리고 dispatch의 기준이 기본은 entry이고
필요하면 container라는 러프한 룰을 적용하다보니 더이상 기준이 아니게 되었고,
props가 강제된 컴포넌트는 재사용이 불가능해졌습니다.
ޖਸ֬ଢ଼ਸө
action, reducer, middleware ..
그리고 이건 주위에서 ‘redux를 사용하지 않는 이유’ 중 하나로 많이 들었던 이야기 인데요,
redux를 사용하기 위해 반복적으로 적어줘야 하는 코드가 많았습니다.
복사-붙여넣기 하면 손쉽게 작성할 수는 있지만, 중복코드가 늘어나는 작업은 꽤 귀찮은 일이었습니다.
Slide 72
Slide 72 text
ޖਸ֬ଢ଼ਸө
Data ?
Project
view
data
util
api
helper
Project
Data View .
Slide 73
Slide 73 text
ޖਸ֬ଢ଼ਸө
Data ?
마지막으로 놓친점은, 제가 너무 Data만 신경썼다는 점입니다.
이전 구조에서 view와 data가 너무 심하게 바인딩 되어 있었다는 점에 포커스를 맞추다 보니
다른 부분들을 많이 놓쳤습니다.
하나의 프로젝트는 data와 view만으로 구성되지 않습니다. view, data외에도
api, util, view를 위한 helper function등이 프로젝트의 구성요소가 될 수 있습니다.
Slide 74
Slide 74 text
ޖਸ֬ଢ଼ਸө
Data ?
Project
view
data
util
api
helper
helper ?
view-model ..?
controller ?
?
Slide 75
Slide 75 text
ޖਸ֬ଢ଼ਸө
Data ?
놓쳤던 문제를 조금더 자세히 설명드리자면 view에 필요한 helper의 위치를 어디로 둘것인지
정하지 않았습니다.
helper라고 해서 뭔가 좀 작아보이긴 하지만 사실 일종의 ‘로직처리’로 볼 수 있는 부분인데,
이를 명확히 정하지 않아서 로직이 곳곳에 흩뿌려진 상태가 되었습니다.
Slide 76
Slide 76 text
ޖਸ֬ଢ଼ਸө
Data ?
Project
view
data
util
api
helper
util
?
view Data
util ?
Slide 77
Slide 77 text
ޖਸ֬ଢ଼ਸө
Data ?
그리고, helper와 유틸의 차이에 대한 기준을 명확히 하지 않았습니다.
명확한 목적을 구분하지 않아 비슷한 역할을 하는 함수가 util에 있거나,
helper function이 정의된 곳에 있거나..
이렇게 케이스가 나뉘면서 다른사람이 보았을때 혹은 미래의 내가 보았을때 코드가 쉽게 파악되지
않는 문제가 있었습니다.
Slide 78
Slide 78 text
ೠӒܶझѱ౭٘
Slide 79
Slide 79 text
۞ೠਬ۽
CMS
: CMS ?
4
: , . ?
몇가지 아쉬웠던 점이 있지만 기존에 사용하던 구조에서
break change를 이뤄냈다는 것과
가설을 실제 작은 서비스 레벨로 구현하여 검증했다는 점에
우선 의미를 두기로 했습니다.
Slide 80
Slide 80 text
۞ೠਬ۽
( ! )
하지만 역시 다 만들고 나서야 아까같은 스파게티 상태가 눈에밟혔습니다.
이를 개선하기 위해 다 부수고 다시 만드는 것은 일정상 불가능한 일이었으므로 이 프로젝트는 이대로
유지보수 하는것으로 하고, 묻고 다음 프로젝트에서 더블로 잘해보기로 했습니다.
components / container / routes / view
domain
먼저 component는 domain을 알지못하는,
항상 재사용을 고려해서 작성되어야 하는 부분입니다.
Slide 90
Slide 90 text
components / container / routes / view
domain
Primitive Type Props .
interface Props {
name: string;
value: string;
}
component는 무조건 primitive 타입의 props만 가집니다.
개발자가 따로 정의한 entity를 props로 가질 수 없는 부분입니다.
entity를 가지게 되면 도메인을 알게되고,
이렇게 되면 다른곳에서 재사용을 하지 못할 확률이 높아지기 때문입니다.
Slide 91
Slide 91 text
components / container / routes / view
domain Props .
,
container는 domain props를 가질수도 있는 부분으로 정의하였습니다.
이 부분은 재사용을 할수도, 하지 않을 수도 있습니다.
재사용성은 컨테이너에게 주요한 관심사가 아닙니다.
Slide 92
Slide 92 text
components / container / routes / view
domain Props .
,
Entity Type Props .
interface Props {
poke: Pokemon;
count: number;
}
예시를 들자면, 이렇게 개발자가 정의한 custom entity인 Pokemon을
props로 가질 수 있습니다.
여기 예시 코드를 보면 custom entity인 Pokemon과
primitive type인 count를 props로 가지고 있습니다.
Slide 93
Slide 93 text
components / container / routes / view
domain Props .
,
Entity Type Props .
, dispatch
+ ) tmi: react-redux 7.1
도메인을 알게되는 부분이기 때문에
자연스럽게 dispatch는 컨테이너 레벨에서 이루어지게 됩니다.
그리고, eact-redux 7.1 버전의
useSelector와 useDispatch를 사용하게 되면서 코드량이 확 줄었습니다.
connect와 mapdispatchToProps등을 정의할 필요가 없어져서
훨씬 간결하게 사용할 수 있으니 이 문법을 사용해보시는 것을 추천드립니다.
Slide 94
Slide 94 text
components / container / routes / view
Route : component view
View : container
route쪽에서 routing을 담당하고, 여기에 매칭되는 component가 view입니다.
view에서는 비즈니스 로직없이 layout만 담당하게 됩니다.
Slide 95
Slide 95 text
"OE
,
Project
view
data
util
api
helper
아까 소개되었던 프로젝트의 5가지 구성요소 입니다.
Slide 96
Slide 96 text
Project
view
data
util
api
helper
"OE
,
: helper
controller
!
1 (util )
view(container || component)
Slide 97
Slide 97 text
"OE
,
모호했던 helper function에 대해서는 이렇게 정리했습니다.
helper의 위치는 component혹은 container 하위의 controller라는 폴더에 위치해야 한다는 규
칙을 세웠습니다.
util과는 다르게 view를 위한 함수이며 1회성에 가까운 역할을 한다고 정의했습니다.
Slide 98
Slide 98 text
Project
view
data
util
api
helper
"OE
,
: util
,
view !
Slide 99
Slide 99 text
"OE
,
반대로 util은 특정 component나 container가 아니라 어디서든 범용적으로 사용가능해야 하며,
자연히 추상화 레벨이 높아야 합니다. 너무 많은 결정을 하는 함수는 util에 두지 않기로 했습니다.
Slide 100
Slide 100 text
࢜۽
: ( ) ,
~ !
: ( )
그리고, 새로운 집에 리덕스를 도입하고 공유하는 과정에서 팀원들이 새집증후군을 느꼈는데요,
Slide 101
Slide 101 text
࢜۽
:
!
:
이를 해소할 무언가가 필요했습니다. 여기서부턴 아까 말씀드렸던 리덕스를 쓰면서 불편했던점에
대한 꿀팁정도로 이해해주시면 좋을것 같습니다.
Slide 102
Slide 102 text
࢜ૐറҵృܻ؋झಞ
action, reducer, middleware .
redux . .
아까 액션, 리듀서, 미들웨어를 정의하는데있어 비슷한 코드를 매번 적어주어야 한다는 불편함이
있었는데요,
생각해보니 redux는 잘못이 없고 제가 조금 잘못사용하고 있지 않나.. 라는 생각이 들었습니다.
오른쪽 사진이 리덕스 새집증후군을 해결하기 위한 actionUtil 함수인데요,
이를 자세히 보자면 이런 구조입니다.
createAsynAction이라는 유틸성 함수를 정의하고 이 함수는 3개의 프리픽스와 액션 함수를 리턴
합니다.
사용하는 쪽에서는 함수 하나만 호출하면 바로 액션정의가 끝나게 됩니다.
미들웨어 또한 이런식으로 작성해서 중복코드를 줄였습니다.
Slide 105
Slide 105 text
reducer
switch - case
action, reducer, middleware .
࢜ૐറҵృܻ؋झಞ
리듀서 쪽에서도 기존의 switch-case 문법을 걷어냈습니다.
여기 보시는건 로딩 리듀서인데요, fetchState를 관리하는 것은
모든 리듀서에서 필요했던 요소여서 로딩 리듀서 또한 유틸성으로 분리했습니다.
Slide 106
Slide 106 text
: redux actionUtil !
!
이렇게 액션유틸을 만들고 팀원들에게 공유해본결과,
Slide 107
Slide 107 text
초기셋팅 비용이 많이 들고 중복코드가 많아서 괴로웠던 점이 어느정도 해소 되었다는 답을 들을
수 있었습니다.
Slide 108
Slide 108 text
( )
이런 점들을 고쳐나가며 프로젝트를 만들어나가고 있는데, 아직까지는 괜찮은 구조인것 같습니다.
각각이 어디에 있는지 명확하며 리뷰하기에 쉬운 코드를 만들기 위해 열심히 노력하고 있습니다.
#FTU1SBDUJDFۆ
바로 BestPractice를 찾기 위해서 입니다. 그런데 Best Practice란 무엇일까요?
Slide 111
Slide 111 text
#FTU1SBDUJDFۆ
❌
저는 아직 못찾았고, 사실 그런건 없는게 아닌가.. 라는 생각도 듭니다.
Slide 112
Slide 112 text
#FTU1SBDUJDFۆ
왜냐하면, ‘좋다’ 라는 정의는 팀의 상황과 서비스에 따라 달라질 수 있기 때문입니다.
그래서, 좋은 패턴을 위해서는, 팀 구성원이 머리를 맞대고 합의해나가는 과정이 필요합니다.
문제점을 의식하고 공유하여 우리팀에 맞는 더 나은 구조를 계속 찾아가보고 있습니다.
Slide 113
Slide 113 text
: ( )
: ( ) !
그리고, 저장소가 분리되고 매번 구조를 Set up하는 것은 비용이 많이들고 힘든 일이 될 수 있습니다.
이를 개선하기 위해 Project Scaffolding 도구를 만들었습니다.
Slide 114
Slide 114 text
sample project
template repository를 만들고, 각 폴더에 변경된 구조를 아카이빙 하기로 했습니다.
github api를 활용해서 api call까지 하는 형태의 샘플 프로젝트를 만들어서 아카이빙 하고 있습니다.
Slide 115
Slide 115 text
.
sample project
sca olding
이렇게 샘플프로젝트를 만들면 새로운 구조에 대해서 누구나 쉽고 간단하게 검증해볼 수 있습니다.
저희 팀에서 관리하는 대부분의 프로젝트는 api response를 이용하여 이를 가공하는 형태의 프로젝트
이기 때문이죠.
덤으로, 스캐폴딩도구도 만들 수있으니 서비스별로 저장소를 분리한 저희팀에 유용한 방식이 아닌가..
하고 생각하고 있습니다.
Slide 116
Slide 116 text
( )
이런저런 과정을 거쳐서 아직 구조는 조정중에 있습니다.
지금 사용하고 있는 구조역시, 결국 불편한 점이 있다면 다른 구조를 선택할 수 있습니다.
더 많은 검증이 필요하고 계속해서 불편한점에 귀기울이며 프로젝트는 계속 구조조정 중입니다.